Last time I had to prepare a firmware for FPGA, that contained a complex hierarchy of blocks and subbblocks, containing registers and arrays of registers on different levels of hierarchy. Those registers were connected to simple block driven by IPbus (
TOP |
+-SREG: top_status +-CREG: sys_control +-CREG: resets | +-N_OF_A x ABlocks | | | +-SREG: A_status | +-CREG: A_control | +-N_OF_I2C_SLAVES x I2CBlock | | | | | +-CREG: I2C_Config | | +-SREG: I2C_Status | | +-CREG: I2C_Command | | | +-N_OF_SPI_SLAVES x SPIBlock | | | +-CREG: SPI_Config | +-SREG: SPI_Status | +-CREG: SPI_Tx | +-SREG: SPI_Rx | +-N_OF_B x BBlocks | +- N_OF_CELLS x CREG: Out_data +- N_OF_CELLS x SREG: In_data +- SREG: B_status +- CREG: B_configMaintaining of addresses in the above structure, especially when the constants "N_OF..." are changing, is really a nightmare. In my proposed solution, the abover structure may be described in a simple Python file:
#!/usr/bin/python3 from addr_gen import *
#Definitions of constants used in the package c.ADDR_VERSION=int(time.time()) c.N_OF_A = 13 c.N_OF_I2C_SLAVES = 6 c.N_OF_SPI_SLAVES = 8 c.N_OF_B = 5 c.N_OF_CELLS = 12
#Define registers in the BBlock bbl_def=aobj("BBLOCK",[ ("out_data",sreg_def,c.N_OF_CELLS), ("in_data",sreg_def,c.N_OF_CELLS), ])
#Define registers in SPI block spi_def=aobj("SPI",[ ("spi_config",creg_def), ("spi_status",sreg_def), ("spi_tx",creg_def), ("spi_rx",sreg_def), ])
#Define registers in I2C block i2c_def=aobj("I2C",[ ("i2c_config",creg_def), ("i2c_status",sreg_def), ("i2c_command",creg_def), ])
#Define registers and subblocks in the ABlock abl_def=aobj("ABLOCK",[ ("a_status",creg_def), ("a_control",creg_def,2), #Two registers, supporting up to 64 links ("spi",spi_def,c.N_OF_SPI_SLAVES), ("i2c",spi_def,c.N_OF_I2C_SLAVES), ])
#Define registers and subblocks in the TOP block top_def=aobj("TOP",[ ("addr_ver",sreg_def), ("top_st",sreg_def), ("sys_ctrl",sreg_def), ("resets",creg_def), ("ab",abl_def,c.N_OF_A), ("bb",bbl_def,c.N_OF_B), ])
#Generate package with constants gen_vhdl_const_package("top_const_pkg")
#Generate package with address related types and addresses gen_vhdl_addr_package("top_adr_pkg","",crob_def,0,0)
#Generate Python module with addresses gen_python_addr_module("top_adr",crob_def,0,0)
Running the above, generates the appropriate VHDL packages with constants and addresses, and python module with addresses. Connection of signals in the VHDL code may be done as follows: (I assume, that each blocks provides inputs from control registers and output to status registers in hierarchical records like below: type T_I2C_CTRL is record i2c_config : std_logic_vector(31 downto 0); i2c_command : std_logic_vector(31 downto 0); end record T_I2C_CTRL; type T_I2C_CTRL_ARR is array(natural range) of T_I2C_CTRL;
type T_SPI_CTRL is record spi_config : std_logic_vector(31 downto 0); spi_command : std_logic_vector(31 downto 0); end record T_SPI_CTRL; type T_SPI_CTRL_ARR is array(natural range) of T_SPI_CTRL;
type T_ABL_CTRL is record a_control: std_logic_vector(31 downto 0); spi : T_SPI_CTRL_ARR(0 to N_OF_SPI_SLAVES-1); i2c : T_I2C_CTRL_ARR(0 to N_OF_I2C_SLAVES-1); end record T_ABL_CTRL; )
-- Process for connecting the signals process (all) is begin -- process stat_reg(tad_addr.addr_ver)