axi4lite_Register
The axi4lite_Register is a generic implementation of memory-mapped-registers (MMR) providing an AXI4-Lite communication interface. The register layout is describe by a generic constant called CONFIG. This constant is constructed by various helper functions as described in the following sections.
Instantiation
Depending on the complexity of the register (number of registers, register modes, repeated groups), various VHDL coding styles (also with vary complexity) can be used to configure the axi4lite_Register completely in VHDL. No external scripting or code generation is needed to describe and use the AXI4-Lite register.
Simple Register
The example code on the right side demonstrates a simple 32-bit AXI4-Lite register offering two registers at
addresses 0x00 and 0x04. The first register is configured in read-write mode, the second
one is in read-only mode. The register description array is provided directly in the generic map to the
CONFIG parameter within the register instantiation.
The port map connect the system signals Clock and Reset as well as the AXI4-Lite interface consisting of AXI4Lite_m2s, AXI4Lite_s2m and AXI4Lite_irq to the AXI4-Lite infrastructure.
For this simple read and write use-model, only ports RegisterFile_ReadPort and RegisterFile_WritePort are required.
Hint
The naming is chosen from fabric view, thus writing is from fabric to register and reading is from register to fabric.
The following code accesses a single register by it’s configuration index according to the configuration generic (this is not the register offset).
signal my_Value32Bit : std_logic_vector(31 downto 0);
my_Value32Bit <= ReadPort(0); -- register 'Value'
A register value can be provided from VHDL code as follows:
signal my_Status32Bit : std_logic_vector(31 downto 0);
WritePort(1) <= my_Status32Bit; -- register 'Status'
myReg_blk : block
signal ReadPort : T_SLVV(0 to 1)(31 downto 0);
signal WritePort : T_SLVV(0 to 1)(31 downto 0);
begin
Reg : entity PoC.axi4lite_Register
generic map (
CONFIG => (
0 => to_AXI4_Register(Name => "Value", Address => 32x"0", rw_config => ReadWrite),
1 => to_AXI4_Register(Name => "Status", Address => 32x"4", rw_config => ReadOnly)
)
)
port map (
Clock => Clock,
Reset => Reset,
AXI4Lite_m2s => ConfigRegister_m2s,
AXI4Lite_s2m => ConfigRegister_s2m,
AXI4Lite_irq => open,
RegisterFile_ReadPort => ReadPort, -- read from fabric
RegisterFile_WritePort => WritePort -- write from fabric
);
Register Description from Generator Function
A register description is an array of type T_AXI4_Register_Vector. Such an array can be constructed by an
aggregate expression (see Simple Register), by calling a user-defined helper function or
by concatenating results from multiple user-defined helper functions.
The example code on the right side demonstrates how local signals can be sized based on a CONFIG
constant.The constant itself is computed by a user-defined function. See section
Configuration for details.
When a register is access from AXI4-Lite side, a hit event (strobe) is generated. In case an AXI4-Lite read operation was executed and a matching register offset was decoded, a corresponding bit is active for one clock cycle in RegisterFile_WritePort_hit. Similarly, in case an AXI4-Lite write operation was executed and a matching register offset was decoded, a corresponding bit within RegisterFile_ReadPort_hit is asserted for one clock cycle.
Advantages of a generator function
Employing a user-defined helper function offer multiple advantages:
Encapsulate register description generation in a local or global VHDL function.
Use VHDL language constructs like concatenation, loops, if-then-else or other subprogram to construct a description.
Automate register names and register offset incrementation.
Generate registers based on function parameters (e.g. from generics).
Store the register description in a constant.
Lookup register indices by name.
myReg_blk : block
constant CONFIG : T_AXI4_Register_Vector := genConfig;
signal ReadPort : T_SLVV( 0 to CONFIG'length - 1)(31 downto 0);
signal ReadPort_hit : std_logic_vector(0 to CONFIG'length - 1);
signal WritePort : T_SLVV( 0 to CONFIG'length - 1)(31 downto 0);
signal WritePort_hit : std_logic_vector(0 to CONFIG'length - 1);
signal WritePort_strobe : std_logic_vector(0 to CONFIG'length - 1) := get_StrobeVector(CONFIG);
begin
Reg : entity PoC.axi4lite_Register
generic map (
CONFIG => CONFIG
)
port map (
Clock => Clock,
Reset => Reset,
AXI4Lite_m2s => ConfigRegister_m2s,
AXI4Lite_s2m => ConfigRegister_s2m,
AXI4Lite_irq => open,
RegisterFile_ReadPort => ReadPort,
RegisterFile_ReadPort_hit => ReadPort_hit,
RegisterFile_WritePort => WritePort
RegisterFile_WritePort_hit => WritePort_hit,
RegisterFile_WritePort_strobe => WritePort_strobe
);
Interface
The IP core offers a system interface (clock, reset), the AXI4-Lite interface and access to the internal registers from fabric.
Attention
The naming of fabric ports is from fabric point-of-view. However, the naming of register modes like ReadOnly is
from AXI4-Lite manger (CPU, software) point-of-view.
Generics
CONFIG
- Name:
CONFIG
- Type:
AXI4Lite:T_AXI4_Register_Vector- Default Value:
— — — —
- Description:
Register description as an array of
AXI4Lite:T_AXI4_Registervalues. Usually, these array elements are constructed by the helper function to_AXI4_Register.See Configuration
INTERRUPT_IS_STROBE
- Name:
INTERRUPT_IS_STROBE
- Type:
boolean- Default Value:
true- Description:
Define the behavior of the interrupt request port AXI4Lite_irq.
If this generic is
true, a edge (strobe) interrupt is generated, otherwise a level (flag) interrupt.Todo
With this generic, it can be selected if the Interrupt-pin should through an interrupt as
StrobeorValue. By selectingStrobe, the module will block a new interrupt until theINTERRUPT_MATCH_REGISTERis read out.
INTERRUPT_ENABLE_REGISTER_ADDRESS
- Name:
INTERRUPT_ENABLE_REGISTER_ADDRESS
- Type:
unsigned- Default Value:
x"00"- Description:
If Interrupts are used, this generic selects the address of the internal
INTERRUPT_ENABLE_REGISTER.
INTERRUPT_MATCH_REGISTER_ADDRESS
- Name:
INTERRUPT_MATCH_REGISTER_ADDRESS
- Type:
unsigned- Default Value:
x"04"- Description:
If Interrupts are used, this generic selects the address of the internal
INTERRUPT_MATCH_REGISTER.
INIT_ON_RESET
- Name:
INIT_ON_RESET
- Type:
boolean- Default Value:
true- Description:
The Init-value of the registers, that is set by the
Config, is set by default with the Reset. This can be disabled here. This helps with reducing control-sets and therefore helps by CLB utilization.
IGNORE_HIGH_ADDRESS
- Name:
IGNORE_HIGH_ADDRESS
- Type:
boolean- Default Value:
true- Description:
The module will calculate based on the configuration how many bits are needed to address every specified register. If this generic is set, it will ignore every bit which is coming after the needed address-bits. These bits are considered as base address. By setting this value, you can pass the full 40/32bit from Zynq, and it will filter out the base address for it.
RESPONSE_ON_ERROR
- Name:
RESPONSE_ON_ERROR
- Type:
AXI4Lite:T_AXI4_Response- Default Value:
C_AXI4_RESPONSE_DECODE_ERROR- Possible Values:
C_AXI4_RESPONSE_OKAY,C_AXI4_RESPONSE_EX_OKAY,C_AXI4_RESPONSE_SLAVE_ERRORorC_AXI4_RESPONSE_DECODE_ERROR- Description:
With this generic can be selected which response code should be sent out if an address is accessed that is not handled by
Config.
DISABLE_ADDRESS_CHECK
- Name:
DISABLE_ADDRESS_CHECK
- Type:
boolean- Default Value:
false- Description:
The module is internally calculating if any registers have overlapping addresses and will create an error if so. This check takes a bit of synthesis time that depends on the size of
Config. This check can be disabled.Attention
This is not recommended!
DEBUG
- Name:
DEBUG
- Type:
boolean- Default Value:
false- Description:
If set to true, the module will print the configuration and settings into the synthesis-log with
assert.- Description:
If set to true, the module sets specific internal signals as mark-debug. These signals are the hit-vectors, decoded addresses, and interrupt signals.
Ports
Clock
- Name:
Clock- Type:
std_logic- Mode:
in
- Default Value:
— — — —
- Description:
Clock
Reset
- Name:
Reset- Type:
std_logic- Mode:
in
- Default Value:
— — — —
- Description:
synchronous high-active reset
AXI4Lite_m2s
- Name:
AXI4Lite_m2s- Type:
axi4lite.T_AXI4Lite_Bus_m2s- Mode:
in
- Default Value:
— — — —
- Description:
AXI4-Lite manager to subordinate signals.
AXI4Lite_s2m
- Name:
AXI4Lite_s2m- Type:
axi4lite.T_AXI4Lite_Bus_s2m- Mode:
out
- Default Value:
— — — —
- Description:
AXI4-Lite subordinate to manager signals.
AXI4Lite_irq
- Name:
AXI4Lite_irq- Type:
std_logic- Mode:
out
- Default Value:
— — — —
- Description:
AXI4-Lite interrupt request.
Functionality depends on configured generics.
RegisterFile_ReadPort
- Name:
RegisterFile_ReadPort- Type:
T_SLVV(0 to CONFIG'length - 1)(31 downto 0)- Mode:
out
- Default Value:
— — — —
- Description:
Read-Port for register values (to fabric).
An array of 32-bit words; one 32-bit word per register.
RegisterFile_ReadPort_hit
- Name:
RegisterFile_ReadPort_hit- Type:
std_logic_vector(0 to CONFIG'length - 1)- Mode:
out
- Default Value:
— — — —
- Description:
Hit-vector to fabric. A bit is asserted if the a AXI4-Lite manager has written a specific register and therefore changed the value in the corresponding register.
RegisterFile_WritePort
- Name:
RegisterFile_WritePort- Type:
T_SLVV(0 to CONFIG'length - 1)(31 downto 0)- Mode:
in
- Default Value:
— — — —
- Description:
Write-Port for register values (from fabric).
An array of 32-bit words; one 32-bit word per register.
RegisterFile_WritePort_hit
- Name:
RegisterFile_WritePort_hit- Type:
std_logic_vector(0 to CONFIG'length - 1)- Mode:
out
- Default Value:
— — — —
- Description:
Hit-vector to fabric. A bit is asserted if the a AXI4-Lite manager has read a specific register and therefore fetched the value in the corresponding register.
RegisterFile_WritePort_strobe
- Name:
RegisterFile_WritePort_strobe- Type:
std_logic_vector(0 to CONFIG'length - 1)- Mode:
in
- Default Value:
— — — —
- Description:
By asserting a bit to
'1', the corresponding value atRegisterFile_WritePortis captured into the corresponding register.
The default value is set by the functionget_strobeVector(CONFIG).Todo
Overwrite is mostly needed if
rw_configis set to readWriteable.
Configuration
Register Description
The configuration can be created in many different ways. If the register is small, it can be done like above by directly setting the register in the generic. This approach has the disadvantage that the index of the read and write port is changing if an register is added later. This can easily create errors inside the design, if not each and every index is checked and updated if needed. This is why this approach is not commanded.
The next step is to save the Config inside a constant. If done like this, the register can also use the Name-field. If all register are named, the index of the register can be calculated by helper-functions with its specific name (See next section Helper Functions). This helps to prevent from errors and allows easily to extend the register because the index is calculated new and updated appropriate. This approach is only suitable for mid-complex registers.
function genConfig return T_AXI4_Register_Vector is
variable temp : T_AXI4_Register_Vector(0 to 511);
variable addr : natural := 0;
variable pos : natural := 0;
begin
temp(pos) := to_AXI4_Register(Name => "System.Version", Address => to_unsigned(addr, 32), Init_Value => std_logic_vector(to_unsigned(3, 32)), rw_config => constant_fromInit);
addr := addr +4; pos := pos +1;
temp(pos) := to_AXI4_Register(Name => "System.Status",Address => to_unsigned(addr, 32), rw_config => readable);
addr := addr +4; pos := pos +1;
addr := 256;
temp(pos) := to_AXI4_Register(Name => "System.Command",Address => to_unsigned(addr, 32), rw_config => readWriteable, Auto_Clear_Mask => x"FFFFFFFF");
addr := addr +4; pos := pos +1;
return temp(0 to pos -1);
end function;
constant CONFIG : T_AXI4_Register_Vector := gen_config;
This example configuration creates a total of three registers. After one register is created the index/position is
incremented by one and the address by 4. The addresses are adapting automatically. As can be seen, the third register is
shifted to address 0x100 by overwriting the addr variable. This creates an empty space between 0x4 and 0x100, which is
creating a RESPONSE_ON_ERROR code while read or write access.
In this function registers can be created also as a loop:
for i in 0 to Num_RTT -1 loop
temp(pos) := to_AXI4_Register(Name => "Data_Value(" & integer'image(i) & ")", Address => to_unsigned(addr, 32), rw_config => readable);
pos := pos +1; addr := addr +4;
end loop;
By giving each loop-register a different name dependent on the constant i, it can be referenced separately.
Record Data Structure
The configuration specified via CONFIG generic determines the functionality of the
register. It’s an array of the AXI4Lite.pkg:T_AXI4_Register_Description. This record has the following elements:
Field |
Type |
Description |
|---|---|---|
Name |
string(1 to 64) |
A Name as string can be selected for this register. |
Address |
unsigned(31 downto 0) |
The Address of this register. |
rw_config |
T_ReadWrite_Config |
See chapter |
Init_Value |
std_logic_vector(31 downto 0) |
The initial value after reset or boot-up of the register. |
Auto_Clear_Mask |
std_logic_vector(31 downto 0) |
Auto-clear-mask if rw_config is readWriteable. Bits set in this maks are given out only as a strobe and cleared with the next clock-cycle. |
Is_Interrupt_Register |
boolean |
Selects if this register can create and interrupt or not. See section Interrupt. |
Register Modes
For each register, a mode can be selected with the field rw_config. This mode depends on the functionality that should be achieved. Possible modes are:
ConstantValue
- Mode:
ConstantValue
- Behavior:
constant value
- Description:
This Mode connects the
Init_Valuedirectly to the read-mux. No Flip-Flop is created and the Read and Write Port is unconnected.
ReadOnly
- Mode:
ReadOnly
- Description:
This Mode is used to provide a Status register.
ReadOnly_NotRegistered
- Mode:
ReadOnly_NotRegistered
- Description:
As
readablebut does not create flip-flops internally. The WritePort is directly connected to the read-mux. Can be used if data is already driven by registers or if path is not time-critical.
ReadWrite
- Mode:
ReadWrite
- Description:
This Mode can be used to configure or control the PL form SW. Software can write into this register, and it will be visible through RegisterFile_ReadPort. The PL can also overwrite the Value by setting the corresponding bit in port RegisterFile_WritePort_strobe. In this mode, the field
Auto_Clear_Maskis active. It can be used to control FSM’s with a command, that is set only for one CC.
ReadWrite_NotRegistered
- Mode:
ReadWrite_NotRegistered
- Description:
This mode is used only for special use-cases. It provides the internal read- and write-mux connections directly to the ports, so any external functionality can be implemented. Data on the ``RegisterFile_ReadPort`` is only valid if ``RegisterFile_WritePort_strobe`` is set! See also chapter Special Functionality.
LatchValue_ClearOnRead
- Mode:
LatchValue_ClearOnRead
- Description:
Interrupt CapableAfter Reset or boot-up, this latch is cleared and can accept new data. If Stobe is then set, the data fromRegisterFile_WritePortis saved. If this value is unequal to Init_Value, the value can not be overwritten until the SW reads it out. If latching condition is met and register is set as Is_Interrupt_Register, and interrupt is thrown.
LatchValue_ClearOnWrite
- Mode:
LatchValue_ClearOnWrite
- Description:
Interrupt CapableSame as latchValue_clearOnRead, but it is only cleared by actively writing into this register. The written value is ignored.
LatchHighBit_ClearOnRead
- Mode:
LatchHighBit_ClearOnRead
- Description:
Interrupt CapableBy setting Strobe, the value ofRegisterFile_WritePortis logically or-red together with the current register content. So a one-bit is always added to the value. By reading this register out, the software gets the value and clears it as well.
LatchHighBit_ClearOnWrite
LatchLowBit_ClearOnRead
- Mode:
LatchLowBit_ClearOnRead
- Description:
Interrupt CapableFor low-active signals. By setting Strobe, the value ofRegisterFile_WritePortis logically and-ed together with the current register content. So a zero-bit is always added to the value. By reading this register out, the software gets the value and sets all bits as well.
LatchLowBit_ClearOnWrite
Reserved
Helper Functions
For ease of use, functions are created to help for basic modifications of the configuration.
filter_Register_Vector
function filter_Register_Vector(str : string; description_vector : T_AXI4_Register_Vector) return T_AXI4_Register_Vector;
Removes all elements of description_vector where description_vector(i).name(str’range) /= str.
function filter_Register_Vector(char : character; description_vector : T_AXI4_Register_Vector) return T_AXI4_Register_Vector
Removes all elements of description_vector where description_vector(i).name(1) /= char.
add_Prefix
function add_Prefix(prefix : string; Config : T_AXI4_Register_Vector; offset : unsigned(Address_Width -1 downto 0) := (others => '0')) return T_AXI4_Register_Vector;
Adds the string prefix prefix to each Config(x).Name and adds the offset value to the Config(x).Address. This
function can be used if multiple standardized (as constant) register need to be put with a prefix into the big register.
Here is an example:
constant Config_Packetizer : T_AXI4_Register_Vector := (
0 => to_AXI4_Register(Name => "CMD", Address => to_unsigned(0, 32), rw_config => readWriteable)
1 => to_AXI4_Register(Name => "CMD2", Address => to_unsigned(4, 32), rw_config => readWriteable)
2 => to_AXI4_Register(Name => "STATUS", Address => to_unsigned(8, 32), rw_config => readable));
function gen_config return T_AXI4_Register_Vector is
variable temp : T_AXI4_Register_Vector(0 to 511);
variable addr : natural := 0;
variable pos : natural := 0;
begin
for i in 0 to 1 loop
temp(pos to pos + Config_Packetizer'length -1) := add_prefix("Packetizer(" & integer'image(i) & ").", Config_Packetizer, to_unsigned(addr, 32));
pos := pos + Config_Packetizer'length;
addr := addr + Config_Packetizer'length *4;
end loop;
return temp(0 to pos -1);
end function;
This will result in a config that looks like this:
0 => (Name => "Packetizer(0).CMD", Address => 32x"0", rw_config => readWriteable),
1 => (Name => "Packetizer(0).CMD2", Address => 32x"4", rw_config => readWriteable),
2 => (Name => "Packetizer(0).STATUS", Address => 32x"8", rw_config => readable),
3 => (Name => "Packetizer(1).CMD", Address => 32x"C", rw_config => readWriteable),
4 => (Name => "Packetizer(1).CMD2", Address => 32x"10", rw_config => readWriteable),
5 => (Name => "Packetizer(1).STATUS", Address => 32x"14", rw_config => readable),
to_AXI4_Register
{-TODO-}
get_addresses
{-TODO-}
get_InitValue
{-TODO-}
get_AutoClearMask
{-TODO-}
get_index
{-TODO-}
get_NumberOfIndexes
{-TODO-}
get_indexRange
{-TODO-}
get_Address
{-TODO-}
get_Name
{-TODO-}
get_strobeVector
{-TODO-}
64-bit Registers
The register has a 64-bit register mode. If the Data-Size of the AXI4Lite record is 64-bit wide, the AXI4Lite-Register will automatically put into 64-bit mode. The SW can then write 64-bit aligned into two 32-bit register at once.
Important
The definition in the config is still done with 32-bit registers. Two 32-bit register are combined to one 64-bit register. The read and write is done in the same CC and is consistent. Note that two Hits will be set for both 32-bit register. If the software is making a read_32 it is not possible to know from PL which of these two registers was actually read out (or written to).
Interrupts
The axi4lite_Register is capable of creating interrupts. The rw_config needs to be a latch type to be interrupt
capable (See section Register Mode (ReadWrite-Config)). In total, 32 registers can be set as interrupt register. This
limitation is due to the interrupt match register width of 32 bits. If an interrupt register is latched, the
interrupt is thrown. If the software registers an interrupt, it needs to read out the interrupt match register to
figure out which interrupt occurred. The order of the bits here is equal to the order of the interrupt registers in the
config.
Example:
Config(i) ; Name ; Address ; Init_Value ; Auto_Clear_Mask ; rw_config ; Is_Interrupt_Register
0 ; System.Version ; 0x00000000 ; 0x00000003 ; 0x00000000 ; constant_fromInit ; false
1 ; System.Test; 0x00000004 ; 0x00000000 ; 0x00000000 ; latchLowBit_clearOnRead ; true
2 ; System.Command ; 0x00000028 ; 0x00000000 ; 0xFFFFFFFF ; readWriteable ; false
3 ; System.Status ; 0x0000002C ; 0x00000000 ; 0x00000000 ; latchLowBit_clearOnRead ; true
This configuration will create in total four registers of which two are interrupt registers. The
interrupt match register bit zero is mapped to System.Test, bit one is mapped to System.Status. If one of these
bits is set, the SW needs to look into the correct address for the value. This means, if after an interrupt bit zero is
set from in the interrupt match register, the SW needs to read address 0x00000004 afterwards to get the
interrupt-causing-value and clear the interrupt reason.
Beside the interrupt match register, there is also the Interrupt enable register. With this, you can switch on and off one specific register to through interrupts. The bit will still be set inside interrupt match register, but it will not create an interrupt.
The addresses of both registers are set through generics. See section Interface.Generics.
By using this feature, the internal configuration will add both registers in this configuration:
Config(i) ; Name ; Address ; Init_Value ; Auto_Clear_Mask ; rw_config ; Is_Interrupt_Register
N ; Interrupt_Enable_Register ; INTERRUPT_ENABLE_REGISTER_ADDRESS ; 0xFFFFFFFF ; 0x00000000 ; readWriteable; false
N+1 ; Interrupt_Match_Register; INTERRUPT_MATCH_REGISTER_ADDRESS ; 0x00000000 ; 0x00000000 ; latchHighBit_clearOnRead; true
Atomic Register
An Atomic Register is an external wiring. This is used to avoid read-modify-write operations from software by
introducing a clear-bit-register, set-bit-register and toggle-bit-register. A constant is prepared for this
register inside AXI4Lite.pkg.vhdl called Atomic_RegisterDescription_Vector. It can be mapped/created inside of the
gen_config function like this:
temp(pos to pos + Atomic_RegisterDescription_Vector'length -1) := add_prefix("My_Atomic_reg.", Atomic_RegisterDescription_Vector, to_unsigned(addr *4, 32));
pos := pos + Atomic_RegisterDescription_Vector'length;
addr := addr + Atomic_RegisterDescription_Vector'length;
Note
The Atomic Register should be at least 64bit aligned. If this is true, the set- and clear- mask can be written at the same time.
Afterwards, the wiring needs to be created in the pl like this:
atomic_blk : block
constant low : natural := get_index("My_Atomic_reg.ATOMIC_Value", config);
constant high : natural := get_index("My_Atomic_reg.ATOMIC_BitClr", config);
signal Atomic_Value : std_logic_vector(31 downto 0) := (others => '0');
signal nextAtomic_Value : std_logic_vector(31 downto 0);
begin
procedure Make_AtomicRegister(
Reset => Reset,
RegisterFile_ReadPort => RegisterFile_ReadPort(low to high),
RegisterFile_WritePort => RegisterFile_WritePort(low to high),
RegisterFile_ReadPort_hit => RegisterFile_ReadPort_hit(low to high),
PL_WriteValue => Overwrite_from_PL_slv,
PL_WriteStrobe => Overwrite_from_PL_strb,
Value_reg => Atomic_Value,
nextValue_reg => nextAtomic_Value
);
Atomic_Value <= nextAtomic_Value when rising_edge(Clock);
end block;
Usage
Command-Status-Error Interface
I/O Register
The IO Register is used to connect a tri-state buffer directly to the register. It consists of two
atomic registers. The first one for IO (Input/Output) and the second one for T (Tristate).
To add it into config:
temp(pos to pos + IO_RegisterDescription_Vector'length -1) := add_prefix("My_IO_reg.", IO_RegisterDescription_Vector, to_unsigned(addr *4, 32));
pos := pos + IO_RegisterDescription_Vector'length;
addr := addr + IO_RegisterDescription_Vector'length;
Afterwards, the wiring needs to be created in the pl like this:
io_reg_blk : block
constant low : natural := get_index("My_IO_reg.IO.ATOMIC_Value", config);
constant high : natural := get_index("My_IO_reg.T.ATOMIC_BitClr", config);
signal IO_Value : std_logic_vector(31 downto 0) := (others => '0');
signal nextIO_Value : std_logic_vector(31 downto 0);
signal T_Value : std_logic_vector(31 downto 0) := (others => '0');
signal nextT_Value : std_logic_vector(31 downto 0);
begin
procedure Make_IORegister(
Reset => Reset,
RegisterFile_ReadPort => RegisterFile_ReadPort(low to high),
RegisterFile_WritePort => RegisterFile_WritePort(low to high),
RegisterFile_ReadPort_hit => RegisterFile_ReadPort_hit(low to high),
Input => Buffe_I_slv,
Output => Buffe_O_slv,
Tristate => Buffe_T_slv,
IO_reg => IO_Value,
nextIO_reg => nextIO_Value,
T_reg => T_Value,
nextT_reg => nextT_Value,
);
IO_Value <= nextIO_Value when rising_edge(Clock);
T_Value <= nextT_Value when rising_edge(Clock);
end block;
Export Configuration
Naming Convention of Registers
The Name field of the configuration is limited to 64-characters. This needs to be a constant and was defined like this. The conversion to the C-Header file needs some properties to work. These properties are therefore defined globally.
Each register needs to be named.
Each register needs to have a unique name.
The Addresses should monotonically increasing (Only for C-Header File).
Multiple Registers can be grouped together with a prefix. The prefix is separated by a dot.
Multiple Prefixes can be made This is currently not possible with the script.
A array of registers can be defined with parenthesis (i). This will create and array in C-Header file.
An array can be done as well on prefixes This is currently not possible with the script.
Export as YAML
Write Register CSV File (deprecated)
A csv file can be written out of the configuration with the function write_csv_file. It is commanded to use it with an
enabled assert statement or writing it into a constant. With assert, you can also see in the synthesis log if
everything was successfully. PROJECT_DIR is a constant inside my_project.vhdl normally located at src/PoC/.
constant success : boolean := write_csv_file(PROJECT_DIR & "gen/Sampling_Register.csv", config);
--or--
assert write_csv_file(PROJECT_DIR & "gen/Sampling_Register.csv", config) report "Error in writing csv-File!" severity warning;
Create C-Header File from CSV
A big advantage of this register is the automatic register handover to the Software. This is done by converting the freshly generated csv file and converting it into a C-Header file. The registers specified in the config are combined into struct’s. The final struct can than be layed over the AXI4Lite-Register Address.
The conversion is done by a python-script from *********************. This python script works but has currently a lot of limitations. It is planed to extend this script and make it accessible to the CI-Runners for automatic conversions.