I struggle with creating a mask to only write certain bits of my data. Currently i read 32bit data over serial. After reading i get the following information:
b'rt 00000000\n'
This has the type bytes. I assume, my 32bit from a certain register get converted to 8 bytes. However, this register has multiple fields, lets say field1 ranges from bit 0 to 4, field2 from bit 4 to 5, field3 from bit 5 to 8 and so on, so they vary in number, length and position, depending on the register. Now i want to read the information, apply a mask over a certain field, modify that field, and write the information back to my serial device. I have written additional functions to get information of the number of fields, fieldoffsets and fieldwidths. What i have achieved so far:
read_value = read_register_byte(s, r, regName) #output: '00000000'
#from hex to int
read_value = int(read_value, 16)
#from int to 32bit-bin without prefix
read_value = format(read_value, '032b')
#define mask range
maskStart = len(read_value) - (offsets[fieldNo] + widths[fieldNo]) #for field0 this results in 28
maskStop = len(read_value) - offsets[fieldNo] #this results in 32
#create mask template (32bit)
mask = [0]*32
j = 0
#insert F for mask range
for i in read_value:
mask[j] = read_value[j]
if j >= maskStart and j < maskStop:
mask[j]='F'
j+=1
#to string
tempMask=""
for i in mask:
tempMask += i
mask = tempMask
read_value
Out[96]: '00000000000000000000000000000000'
mask
Out[97]: '0000000000000000000000000000FFFF'
What im trying to do is to allow only modification of those bits marked with F. Im sure this is very bad practise, however it should work if i could only achieve the following steps: manipulate read_value with my mask, convert my data back to that 8-byte format and write to my serial-device.
Edit:
Field = namedtuple("Field", ["pos_lsb", "width"])
REG_WIDTH = 32 #r.width
REG_HEX_DIGITS = REG_WIDTH // 4
def create_mask(pos_lsb, width):
mask = ((1 << width)-1) << pos_lsb
return mask
def get_field(reg_value, field):
mask = create_mask(*field)
field_value = (reg_value & mask) >> field.pos_lsb
return field_value
def set_field(reg_value, field, new_value):
mask = create_mask(*field)
reg_value = (reg_value & ~mask) | (new_value << field.pos_lsb)
return reg_value
def value_from_string(reg_value_string):
return int(reg_value_string, 16)
def value_to_string(reg_value):
return f"{reg_value:0{REG_HEX_DIGITS}X}"
def write_field(s, r, regName, fieldNo, data):
""" Writes to a single field of a Trixel register """
""" debug block """
# s = open_serial_port()
# r = Registermap()
# regName = 'tcm_control'
# fieldNo = 0 #1st field
# data = 5
""" end of debug block """
regAddress = r.get_register_address(regName)
fields = r.get_register(regName, 'fields')
offsets = r.get_register(regName, 'offsets')
widths = r.get_register(regName, 'widths')
if fieldNo+1 > fields:
print('fieldNo out of range (max fields: ' + str(fields) + '). Aborting..')
return 0
fieldStart = r.baseAddress + regAddress + offsets[fieldNo]
addr = r.baseAddress + regAddress
register_value_string = read_register_byte(s, r, regName)
field = Field(pos_lsb=offsets[fieldNo], width=widths[fieldNo])
new_field_value = data
# Update field
print("Register: " + str(regName) + ", fieldNo: " + str(fieldNo+1) + "(" + str(fieldNo) + ")" + ", offset (hex): " + hex(fieldStart))
print("Fieldname: " + str(r.get_register(regName,'descriptions')[fieldNo]))
print(f"Original register value: {register_value_string}")
reg_val = value_from_string(register_value_string)
field_value = get_field(reg_val, field)
print(f"Original field value: {field_value} (0x{field_value:x} in hex)")
print(f"Setting field to {new_field_value} (0x{new_field_value:x} in hex)")
reg_val = set_field(reg_val, field, new_field_value)
field_value = get_field(reg_val, field)
print(f"New field value: {field_value} (0x{field_value:x} in hex)")
new_reg_val_string = value_to_string(reg_val)
print(f"New register value to write to FPGA: {new_reg_val_string}")
#write to FPGA
cmd = ("mwr {:08X}".format(addr) + " " + new_reg_val_string + " \n").encode()
message = s.write(cmd)
if message != len(cmd):
print('Error in Write! Bytes expected: ' + str(len(cmd)) + ', written: ' + str(message) + ', cmd: ' + str(cmd))
elif message == len(cmd):
print('Write to register ' + str(hex(addr)) + ' OK.')
Working example:
write_field(s, r, 'tcm_control', 3, 1)
Register: tcm_control, fieldNo: 4(3), offset (hex): 0x50000406
Fieldname: Should be released after configuring all TCM registers over AXI
Original register value: 00000071
Original field value: 1 (0x1 in hex)
Setting field to 1 (0x1 in hex)
New field value: 1 (0x1 in hex)
New register value to write to FPGA: 00000071
Write to register 0x50000400 OK.
Failing example:
write_field(s, r, 'tcm_control', 3, 2)
Register: tcm_control, fieldNo: 4(3), offset (hex): 0x50000406
Fieldname: Should be released after configuring all TCM registers over AXI
Original register value: 00000071
Original field value: 1 (0x1 in hex)
Setting field to 2 (0x2 in hex)
New field value: 0 (0x0 in hex)
New register value to write to FPGA: 000000B1
Write to register 0x50000400 OK.
read_register_byte(s, r, 'tcm_control')
Out[15]: '00000031'
I think you are approaching this the wrong way. Please have a look at this (not very commented) code and see if that is what you actually wants:
from collections import namedtuple
REG_WIDTH = 32
REG_HEX_DIGITS = REG_WIDTH // 4
Field = namedtuple("Field", ["pos_lsb", "width"])
def create_mask(pos_lsb, width):
mask = ((1 << width)-1) << pos_lsb
return mask
def get_field(reg_value, field):
mask = create_mask(*field)
field_value = (reg_value & mask) >> field.pos_lsb
return field_value
def set_field(reg_value, field, new_value):
mask = create_mask(*field)
reg_value = (reg_value & ~mask) | (new_value << field.pos_lsb)
return reg_value
def value_from_string(reg_value_string):
return int(reg_value_string, 16)
def value_to_string(reg_value):
return f"{reg_value:0{REG_HEX_DIGITS}X}"
register_value_string = "0F00F0FF"
field1 = Field(pos_lsb=3, width=5)
new_field_value = 0x12
# Update field1.
print(f"Original register value: {register_value_string}")
reg_val = value_from_string(register_value_string)
field_value = get_field(reg_val, field1)
print(f"Original field value: {field_value} (0x{field_value:x} in hex)")
print(f"Setting field to {new_field_value} (0x{new_field_value:x} in hex)")
reg_val = set_field(reg_val, field1, new_field_value)
field_value = get_field(reg_val, field1)
print(f"New field value: {field_value} (0x{field_value:x} in hex)")
new_reg_val_string = value_to_string(reg_val)
print(f"New register value to write to FPGA: {new_reg_val_string}")
Output:
Original register value: 0F00F0FF
Original field value: 31 (0x1f in hex)
Setting field to 18 (0x12 in hex)
New field value: 18 (0x12 in hex)
New register value to write to FPGA: 0F00F097
I believe this is more in line with what you would want. If you want me to explain it further, please let me know.