[casper] Suggested convention for writing registers
Hello all, I've given some thought to the topic of writing (and reading) registers on the ROACH using the python corr module. Often in a design a single register may be sliced into many bits to control various things. The way I've normally seen such a register written in python looks something like this: fpga.write_int('control',19|110|025|12|13) My feeling is that this approach is difficult to maintain - inheriting code from someone else (or even from one's self 6 months down the line) is likely to bring about some confusion in this case, and lead to a fair amount of spelunking through the Simulink model in order to figure out what bit 9 and bit 10 etc. do. On top of this, it places limitations on changing one of the bits later without modifying the other ones - bitwise or functions work well enough when you're over-writing zeros, but if there's something there already it might not work so well. With this in mind, I would like to suggest a convention which I worked out. It uses python modules struct and construct to make code a bit easier to read. (For reference if anyone is unfamiliar: struct - https://docs.python.org/2/library/struct.html construct - http://construct.readthedocs.org/en/latest/ ) In the design I'm working on at the moment (a wideband spectrometer), I wrote a python module with the following in it: # Bitstruct to control the control register on the ROACH control_reg_bitstruct = construct.BitStruct('control_reg', construct.Padding(4), #28-31 construct.BitField('debug_snap_select',3), #25-27 construct.Padding(3), #22-24 construct.Flag('fine_tvg_en'), #21 construct.Flag('adc_tvg'), #20 construct.Flag('fd_fs_tvg'),#19 construct.Flag('packetiser_tvg'), #18 construct.Flag('ct_tvg'), #17 construct.Flag('tvg_en'), #16 construct.Padding(4), #12-15 construct.Flag('fancy_en'), #11 construct.Flag('adc_protect_disable'), #10 construct.Flag('gbe_enable'), #09 construct.Flag('gbe_rst'), #08 construct.Padding(4), #04-07 construct.Flag('clr_status'), #03 construct.Flag('arm'), #02 construct.Flag('man_sync'), #01 construct.Flag('sys_rst') ) #00 This BitStruct makes the code a little bit more readable, tells you what each bit does, and if you've done this declaration right once, then you don't need to worry about whether you'e shifting numbers by the right amount of bits. For the BitFields where several bits are passed, I used a dictionary to make remembering things (and reading the code) easier as well: # Dictionary for selecting the debug_snap_select bit debug_snap_select = { 'coarse_72': 0, 'fine_128':1, 'quant_16':2, 'ct_64': 3, 'xaui_128':4, 'gbetx0_128': 5, 'buffer_72': 6, 'finepfb_72': 7 } So writing to the register for the first time works like this: control_reg = avn.control_reg_bitstruct.parse('\x00\x00\x00\x00') # Create a blank one to use... # Pulse arm and clr_status high, along with setting gbe_enable and adc_protect_disable high control_reg.gbe_enable = True control_reg.adc_protect_disable = True control_reg.clr_status = True control_reg.arm = True fpga.write_int('control', struct.unpack('I', avn.control_reg_bitstruct.build(control_reg))[0]) # The [0] is necessary because the fpga.write_int function wants an integer datatype, and struct.unpack returns a tuple for some reason. # Bring arm and clr_status low again. control_reg.clr_status = False control_reg.arm = False fpga.write_int('control', struct.unpack('I',avn.control_reg_bitstruct.build(control_reg))[0]) Then, for example if you're controlling something with a function and you need to change only one part of what's in the register and leave the rest unaffected, this is quite easy as well: control_reg = control_reg_bitstruct.parse(struct.pack('I',fpga.read_uint('control'))) # Read the data that's in the register already control_reg.debug_snap_select = debug_snap_select['coarse_72'] # Update the desired bit (or bits, using the conveniently provided dictionary in this case) fpga.write_int('control', struct.unpack('I', control_reg_bitstruct.build(control_reg))[0]) # Write the new data back. Does anyone have any thoughts? Would this perhaps help with maintainability and debugging, or is it a bit of overkill, solving a problem that wasn't really there in the first place? I'm eager to hear any feedback from others who may have walked this path before. I've formatted the python code portions of the above message to a fixed-width font, I hope it comes through on the mailing list...
Re: [casper] Suggested convention for writing registers
Hi Paul, Thanks for the link. I looked through the code a bit and it doesn't seem as though the corr library at all. Have I been using the wrong tool for the job? Would you be able to point me to which file I should start with to get a handle on how the casperfpga package works? Regards, James On Fri, Mar 20, 2015 at 9:22 AM, Paul Prozesky paul.proze...@gmail.com wrote: Morning James We do this in already in the casperfpga package: https://github.com/ska-sa/casperfpga It turns out construct is pretty slow, so we just do the conversions and shifts manually. I think currently the fpg files that are automatically parsed by casperfpga are only generated for ROACH2, but it would be easy to add ROACH support. Cheers Paul On 20 March 2015 at 09:08, James Smith jsm...@ska.ac.za wrote: Hello all, I've given some thought to the topic of writing (and reading) registers on the ROACH using the python corr module. Often in a design a single register may be sliced into many bits to control various things. The way I've normally seen such a register written in python looks something like this: fpga.write_int('control',19|110|025|12|13) My feeling is that this approach is difficult to maintain - inheriting code from someone else (or even from one's self 6 months down the line) is likely to bring about some confusion in this case, and lead to a fair amount of spelunking through the Simulink model in order to figure out what bit 9 and bit 10 etc. do. On top of this, it places limitations on changing one of the bits later without modifying the other ones - bitwise or functions work well enough when you're over-writing zeros, but if there's something there already it might not work so well. With this in mind, I would like to suggest a convention which I worked out. It uses python modules struct and construct to make code a bit easier to read. (For reference if anyone is unfamiliar: struct - https://docs.python.org/2/library/struct.html construct - http://construct.readthedocs.org/en/latest/ ) In the design I'm working on at the moment (a wideband spectrometer), I wrote a python module with the following in it: # Bitstruct to control the control register on the ROACH control_reg_bitstruct = construct.BitStruct('control_reg', construct.Padding(4), #28-31 construct.BitField('debug_snap_select',3), #25-27 construct.Padding(3), #22-24 construct.Flag('fine_tvg_en'), #21 construct.Flag('adc_tvg'), #20 construct.Flag('fd_fs_tvg'),#19 construct.Flag('packetiser_tvg'), #18 construct.Flag('ct_tvg'), #17 construct.Flag('tvg_en'), #16 construct.Padding(4), #12-15 construct.Flag('fancy_en'), #11 construct.Flag('adc_protect_disable'), #10 construct.Flag('gbe_enable'), #09 construct.Flag('gbe_rst'), #08 construct.Padding(4), #04-07 construct.Flag('clr_status'), #03 construct.Flag('arm'), #02 construct.Flag('man_sync'), #01 construct.Flag('sys_rst') ) #00 This BitStruct makes the code a little bit more readable, tells you what each bit does, and if you've done this declaration right once, then you don't need to worry about whether you'e shifting numbers by the right amount of bits. For the BitFields where several bits are passed, I used a dictionary to make remembering things (and reading the code) easier as well: # Dictionary for selecting the debug_snap_select bit debug_snap_select = { 'coarse_72': 0, 'fine_128':1, 'quant_16':2, 'ct_64': 3, 'xaui_128':4, 'gbetx0_128': 5, 'buffer_72': 6, 'finepfb_72': 7 } So writing to the register for the first time works like this: control_reg = avn.control_reg_bitstruct.parse('\x00\x00\x00\x00') # Create a blank one to use... # Pulse arm and clr_status high, along with setting gbe_enable and adc_protect_disable high control_reg.gbe_enable = True control_reg.adc_protect_disable = True control_reg.clr_status = True control_reg.arm = True fpga.write_int('control', struct.unpack('I', avn.control_reg_bitstruct.build(control_reg))[0]) # The [0] is necessary because the fpga.write_int function wants an integer datatype, and struct.unpack returns a tuple for some reason. # Bring arm and clr_status low again. control_reg.clr_status = False control_reg.arm = False fpga.write_int('control', struct.unpack('I',avn.control_reg_bitstruct.build(control_reg))[0]) Then, for example if you're controlling something with a function and you need to change only one part of what's
Re: [casper] Suggested convention for writing registers
Morning James We do this in already in the casperfpga package: https://github.com/ska-sa/casperfpga It turns out construct is pretty slow, so we just do the conversions and shifts manually. I think currently the fpg files that are automatically parsed by casperfpga are only generated for ROACH2, but it would be easy to add ROACH support. Cheers Paul On 20 March 2015 at 09:08, James Smith jsm...@ska.ac.za wrote: Hello all, I've given some thought to the topic of writing (and reading) registers on the ROACH using the python corr module. Often in a design a single register may be sliced into many bits to control various things. The way I've normally seen such a register written in python looks something like this: fpga.write_int('control',19|110|025|12|13) My feeling is that this approach is difficult to maintain - inheriting code from someone else (or even from one's self 6 months down the line) is likely to bring about some confusion in this case, and lead to a fair amount of spelunking through the Simulink model in order to figure out what bit 9 and bit 10 etc. do. On top of this, it places limitations on changing one of the bits later without modifying the other ones - bitwise or functions work well enough when you're over-writing zeros, but if there's something there already it might not work so well. With this in mind, I would like to suggest a convention which I worked out. It uses python modules struct and construct to make code a bit easier to read. (For reference if anyone is unfamiliar: struct - https://docs.python.org/2/library/struct.html construct - http://construct.readthedocs.org/en/latest/ ) In the design I'm working on at the moment (a wideband spectrometer), I wrote a python module with the following in it: # Bitstruct to control the control register on the ROACH control_reg_bitstruct = construct.BitStruct('control_reg', construct.Padding(4), #28-31 construct.BitField('debug_snap_select',3), #25-27 construct.Padding(3), #22-24 construct.Flag('fine_tvg_en'), #21 construct.Flag('adc_tvg'), #20 construct.Flag('fd_fs_tvg'),#19 construct.Flag('packetiser_tvg'), #18 construct.Flag('ct_tvg'), #17 construct.Flag('tvg_en'), #16 construct.Padding(4), #12-15 construct.Flag('fancy_en'), #11 construct.Flag('adc_protect_disable'), #10 construct.Flag('gbe_enable'), #09 construct.Flag('gbe_rst'), #08 construct.Padding(4), #04-07 construct.Flag('clr_status'), #03 construct.Flag('arm'), #02 construct.Flag('man_sync'), #01 construct.Flag('sys_rst') ) #00 This BitStruct makes the code a little bit more readable, tells you what each bit does, and if you've done this declaration right once, then you don't need to worry about whether you'e shifting numbers by the right amount of bits. For the BitFields where several bits are passed, I used a dictionary to make remembering things (and reading the code) easier as well: # Dictionary for selecting the debug_snap_select bit debug_snap_select = { 'coarse_72': 0, 'fine_128':1, 'quant_16':2, 'ct_64': 3, 'xaui_128':4, 'gbetx0_128': 5, 'buffer_72': 6, 'finepfb_72': 7 } So writing to the register for the first time works like this: control_reg = avn.control_reg_bitstruct.parse('\x00\x00\x00\x00') # Create a blank one to use... # Pulse arm and clr_status high, along with setting gbe_enable and adc_protect_disable high control_reg.gbe_enable = True control_reg.adc_protect_disable = True control_reg.clr_status = True control_reg.arm = True fpga.write_int('control', struct.unpack('I', avn.control_reg_bitstruct.build(control_reg))[0]) # The [0] is necessary because the fpga.write_int function wants an integer datatype, and struct.unpack returns a tuple for some reason. # Bring arm and clr_status low again. control_reg.clr_status = False control_reg.arm = False fpga.write_int('control', struct.unpack('I',avn.control_reg_bitstruct.build(control_reg))[0]) Then, for example if you're controlling something with a function and you need to change only one part of what's in the register and leave the rest unaffected, this is quite easy as well: control_reg = control_reg_bitstruct.parse(struct.pack('I',fpga.read_uint('control'))) # Read the data that's in the register already control_reg.debug_snap_select = debug_snap_select['coarse_72'] # Update the desired bit (or bits, using the conveniently provided dictionary in this case)
Re: [casper] Suggested convention for writing registers
On Fri, Mar 20, 2015 at 7:08 AM, James Smith jsm...@ska.ac.za wrote: Hello all, I've given some thought to the topic of writing (and reading) registers on the ROACH using the python corr module. Often in a design a single register may be sliced into many bits to control various things. The way I've normally seen such a register written in python looks something like this: fpga.write_int('control',19|110|025|12|13) My feeling is that this approach is difficult to maintain - inheriting code from someone else (or even from one's self 6 months down the line) is likely to bring about some confusion in this case, and lead to a fair amount of spelunking through the Simulink model in order to figure out what bit 9 and bit 10 etc. do. On top of this, it places limitations on changing one of the bits later without modifying the other ones - bitwise or functions work well enough when you're over-writing zeros, but if there's something there already it might not work so well. So it turns out that it is possible to define registers which aren't full word sizes long in tcpborphserver itself - and then the powerpc will do the shifts for you - this is particularly useful, for writes as that then cuts out a network operation (which would be needed to fetch the adjacent bits so that they aren't clobbered by the write). In the fpg file (or on the telnet connection), use optional [:bits] after an offset or a length to define the register - it is fine if such a register overlaps with something else, as long as the name is unique. The downside of all this: It isn't tested at all, as nobody is using it yet. And the code to make it work is a bit tricky - search for the read/write functions in https://github.com/ska-sa/katcp_devel/blob/master/tcpborphserver3/raw.c, for example write_cmd regards marc
Re: [casper] Suggested convention for writing registers
Hello Marc, Thanks for the comments - I've just sat down with Paul and he's mentioned similar things to what you have. The only challenge with that is tcpborphserver3 doesn't run on ROACH yet, only ROACH2 as I understand. I'm going to have a bit of a go at using the casperfpga library anyway and seeing if I can get it to work with ROACH using a work-around which Paul suggested. I'll post my findings to the mailing list next week sometime. Regards, James On Fri, Mar 20, 2015 at 10:01 AM, Marc Welz m...@ska.ac.za wrote: On Fri, Mar 20, 2015 at 7:08 AM, James Smith jsm...@ska.ac.za wrote: Hello all, I've given some thought to the topic of writing (and reading) registers on the ROACH using the python corr module. Often in a design a single register may be sliced into many bits to control various things. The way I've normally seen such a register written in python looks something like this: fpga.write_int('control',19|110|025|12|13) My feeling is that this approach is difficult to maintain - inheriting code from someone else (or even from one's self 6 months down the line) is likely to bring about some confusion in this case, and lead to a fair amount of spelunking through the Simulink model in order to figure out what bit 9 and bit 10 etc. do. On top of this, it places limitations on changing one of the bits later without modifying the other ones - bitwise or functions work well enough when you're over-writing zeros, but if there's something there already it might not work so well. So it turns out that it is possible to define registers which aren't full word sizes long in tcpborphserver itself - and then the powerpc will do the shifts for you - this is particularly useful, for writes as that then cuts out a network operation (which would be needed to fetch the adjacent bits so that they aren't clobbered by the write). In the fpg file (or on the telnet connection), use optional [:bits] after an offset or a length to define the register - it is fine if such a register overlaps with something else, as long as the name is unique. The downside of all this: It isn't tested at all, as nobody is using it yet. And the code to make it work is a bit tricky - search for the read/write functions in https://github.com/ska-sa/katcp_devel/blob/master/tcpborphserver3/raw.c, for example write_cmd regards marc