VHDL
The generated VHDL entity for a store contains the following:
All variables of the store (functions are not supported).
Every variable is available at its own port (parallel interface).
Depending on how the access variable is set, a variable is read-only, write-only, read-write, or not available.
An AXI4 LITE slave interface allows accessing all variables that are at most 4 bytes in size.
Synchronizer implementation is integrated.
Multiple stores can be chained to pass Synchronizer messages over the same protocol stack.
Store entity
- Store_hdl
The generated stores have the following entity definition (for the ExampleFpga store in this case):
entity ExampleFpga_hdl is generic ( SYSTEM_CLK_FREQ : integer := 100e6; SYNC_OUT_INTERVAL_s : real := 0.1; ID : natural := 0; AXI_SLAVE : boolean := true; BUFFER_AFTER_N_VARS : positive := 4; VAR_ACCESS : ExampleFpga_pkg.var_access_t := ExampleFpga_pkg.VAR_ACCESS_RW; SIMULATION : boolean := false ); port ( clk : in std_logic; rstn : in std_logic; var_out : out ExampleFpga_pkg.var_out_t; var_in : in ExampleFpga_pkg.var_in_t := ExampleFpga_pkg.var_in_default; sync_in : in msg_t := msg_term; sync_out : out msg_t; sync_id : out unsigned(15 downto 0); sync_chained_id : in unsigned(15 downto 0) := (others => '0'); sync_chained_in : in msg_t := msg_term; sync_chained_out : out msg_t; sync_out_trigger : in std_logic := '0'; sync_out_hold : in std_logic := '0'; sync_out_irq : out std_logic; sync_out_have_changes : out std_logic; sync_connected : out std_logic; sync_in_busy : out std_logic; s_axi_araddr : in std_logic_vector(31 downto 0) := (others => '-'); s_axi_arready : out std_logic; s_axi_arvalid : in std_logic := '0'; s_axi_awaddr : in std_logic_vector(31 downto 0) := (others => '-'); s_axi_awready : out std_logic; s_axi_awvalid : in std_logic := '0'; s_axi_bready : in std_logic := '0'; s_axi_bresp : out std_logic_vector(1 downto 0); s_axi_bvalid : out std_logic; s_axi_rdata : out std_logic_vector(31 downto 0); s_axi_rready : in std_logic := '0'; s_axi_rresp : out std_logic_vector(1 downto 0); s_axi_rvalid : out std_logic; s_axi_wdata : in std_logic_vector(31 downto 0) := (others => '-'); s_axi_wready : out std_logic; s_axi_wvalid : in std_logic := '0' ); end ExampleFpga_hdl;
- SYSTEM_CLK_FREQinteger := 100e6
Clock frequency of
clk
.- SYNC_OUT_INTERVAL_sreal := 0.1
Interval to send out Synchronizer messages.
- IDnatural := 0
The ID used for Hello messages. If 0, the value is automatically determined.
- AXI_SLAVEboolean := true
Enable the AXI slave interface when set to
true
.- BUFFER_AFTER_N_VARSpositive := 4
Inject buffers after the given number of variables. A lower number increases the latency of handling sync messages, but reduces the combinatorial path length.
- VAR_ACCESSExampleFpga_pkg.var_access_t := ExampleFpga_pkg.VAR_ACCESS_RW
Allowed access of the variables within the store. The default, all read-write, is the most generic, but has the highest resource usage.
To override only a few access settings, assign a function to
VAR_ACCESS
, with an implementation like this:function var_access return ExampleFpga_pkg.var_access_t is variable v : ExampleFpga_pkg.var_access_t; begin v := ExampleFpga_pkg.VAR_ACCESS_RW; v.\t_clk\ := ACCESS_WO; v.\default_register_write_count\ := ACCESS_WO; return v; end function;
See also var_access_t.
- SIMULATIONboolean := false
When
true
, reduce the sync timing, such that the interval is better suitable for (slow) simulation.- clkin std_logic;
System clock.
- rstnin std_logic;
Low-active reset.
- var_outout ExampleFpga_pkg.var_out_t
All variables within the store. See var_out_t for a description and timing.
- var_inin ExampleFpga_pkg.var_in_t := ExampleFpga_pkg.var_in_default
Interface to write variables. See var_in_t for a description and timing.
- sync_inin msg_t := msg_term
Synchronization input. Connect
sync_in
andsync_out
to the protocol stack. Set tomsg_term
to disable synchronization.- sync_outout msg_t
Synchronization output. Connect
sync_in
andsync_out
to the protocol stack.- sync_idout unsigned(15 downto 0)
The used ID for the Hello message. This equals
ID
whenID
is non-zero. Otherwise, a non-zero value is determined. The value should be constant.- sync_chained_idin unsigned(15 downto 0) := (others => ‘0’)
The
sync_id
of the chained store. IfID
is zero, a non-conflicting value is chosen for this store’ssync_id
.- sync_chained_inin msg_t := msg_term
The
sync_out
of a chained store.- sync_chained_outout msg_t
The
sync_in
of a chained store.- sync_out_triggerin std_logic := ‘0’
Trigger an immediate sequence of Synchronizer messages when set to high for one clock cycle. When kept high, multiple sync sequences can be sent back to back.
- sync_out_holdin std_logic := ‘0’
When high, prevent automatically sending out Synchronizer messages.
- sync_out_irqout std_logic
Interrupt flag that indicates that there is at least one Synchronizer message to be passed over
sync_out
.- sync_out_have_changesout std_logic
Flag that indicates that a variable has been changed in the store, and Synchronization is required. Either flag
sync_out_trigger
, or wait tillSYNC_OUT_INTERVAL_s
has passed and synchronization is started.- sync_connectedout std_logic
Flag that indicates that we have a connection with a remote Synchronizer instance.
- sync_in_busyout std_logic
Flag that is high when Synchronizer messages are being processed.
- s_axi_*
AXI4 LITE slave interface. This is a read-write interface for all store variables that are at most 32-bit in size.
Store package
- Store_pkg
The generated package includes all store-specific settings, like initialization values, and types to make handling all store variables easier. The following items are most interesting:
- type var_out_t
This is a record with all variables. This is the output of the store, with current data, and a flag that indicates when data has changed. The fields in the record are generated based on the names in the store. Check the generated package file to see how these names are translated. The record for a store with only one
uint32
variable could look like this:subtype \some_variable__type\ is unsigned(31 downto 0); type \blob__out_t\ is record value : \some_variable__type\; updated : std_logic; end record; type var_out_t is record \some_variable\ : \some_variable__out_t\; end record;
The output timing is exemplified below.
During reset, the initial value is set at the output, 1 in this example. When the value is written, via
var_in
, the AXI interface, or via the Synchronizer,value
is updated andupdated
is flagged for one clock cycle. The flag is set at every write, not only when the data changes. The example above shows such case when the value 2 is set again.When multiple variables are updated in one Synchronizer message, all of these variables flag the
updated
flag at the same time. The latency between writing a variable and when it is on thevar_out
port is unspecified.The value is undefined when the access of a variable is
ACCESS_WO
orACCESS_NA
.
- type var_in_t
This is a record with all variables. This is the input of the store, which can be used to write to variables. Writes are only accepted if the access of a variable is
ACCESS_RW
orACCESS_WO
.Variables can be updated in three ways: via the
var_in
port, via the AXI interface, and via the Synchronizer. If writes happen at the same time, the priority is in the same order. So, writes viavar_in
always override other writes at the same time.The record for a store with only one
uint32
variable could look like this:subtype \some_variable__type\ is unsigned(31 downto 0); type \some_variable__in_t\ is record value : \some_variable__type\; we : std_logic; end record; type var_in_t is record \some_variable\ : \some_variable__in_t\; end record;
When
we
is high, thevalue
is written to the store.In this example, the variable is set to 1 in the second cycle. Consider the case that a microcontroller and the FPGA synchronize a store. If a variable should be read-only for the microcontroller,
we
can be tied high by the FPGA, such that the FPGA updates the variable constantly. In this case, the FPGA effectively forces the value in the store asvalue
. The Synchronizer will send a stream of updates to the microcontroller of these values. The example above shows forcing the value to 2, 3, and 4 in consecutive cycles.
- type var_access_t
This record contains all variables, with its access specifier. For example:
type var_access_t is record \some_variable\: libstored_pkg.access_t; end record;
See also :ref:
access_t <access_t>
.- constant VAR_ACCESS_RWvar_access_t
Set all variables in the store to
ACCESS_RW
.- constant VAR_ACCESS_ROvar_access_t
Set all variables in the store to
ACCESS_RO
.- constant VAR_ACCESS_WOvar_access_t
Set all variables in the store to
ACCESS_WO
.- constant VAR_ACCESS_NAvar_access_t
Set all variables in the store to
ACCESS_NA
.- function merge(constant a, bvar_in_t) return var_in_t
Merge two
var_in
signals into a single value, such that two sources of variables can be fed into the store. If botha
andb
setwe
for the same variable,a
has priority and the value ofb
is ignored (there is no backpressure in place).
Common package
- libstored_pkg
- type access_t
This specifies the access type of a variable:
type access_t is (ACCESS_RW, ACCESS_RO, ACCESS_WO, ACCESS_NA);
- ACCESS_RW
A variable can be read and written by the FPGA, and is synchronized in both directions.
- ACCESS_RO
A variable can be read by the FPGA. Writes are ignored. Synchronization is accepted, but not generated.
- ACCESS_WO
A variable can be read and written by the FPGA. However, Synchronizer messages are generated, but not accepted.
- ACCESS_NA
The variable cannot be accessed by the FPGA. Synchronizer messages are ignored.
- type msg_t
This record holds all signals relevant to passing messages between protocol layers.
type msg_t is record data : std_logic_vector(7 downto 0); last : std_logic; valid : std_logic; accept : std_logic; end record;
- constant msg_termmsg_t
A constant that can be used as
msg_t
sequence terminator. All messages that reach the terminator, are accepted and dropped.
Protocol
The protocol stack can be built by chaining these components using two msg_t
signals.
All layers have a similar interface:
- Generic layer
entity SomeLayer is port ( clk : in std_logic; rstn : in std_logic; encode_in : in libstored_pkg.msg_t; encode_out : out libstored_pkg.msg_t; decode_in : in libstored_pkg.msg_t; decode_out : out libstored_pkg.msg_t; idle : out std_logic ); end SomeLayer;
- encode_in
This holds data to be encoded from the store towards the hardware infrastructure.
- encode_out
This is
encode_in
encoded by this layer, and to be encoded by a lower layer in the stack.- decode_in
The data received from a lower layer in the stack.
- decode_out
The
decode_in
data, process by this layer, to be passed to a higher layer in the stack.- idle
A flag that indicates that this layer is currently doing nothing.
Passing messages follows this scheme, which is resembles the AXI4-Stream Protocol:
data
is accepted whenvalid
andaccept
are high at the same clock edge. In the example above, this happens at edge 2, 3, 6, 9, and 10. There is no order or dependency invalid
andaccept
; both can be high while other is not, indicating ‘there is data’, or ‘the data can be accepted, if any’, respectively.A message consists of a sequence of data bytes.
last
signals the end of the current message. It depends on the protocol layer if and how this is handled.encode_in
anddecode_out
form the pair that connect to a higher layer,encode_out
anddecode_in
is a pair to a lower layer. Theaccept
signal is in the reverse direction as the others. To ease integration,accept
is in the counterpart of the pair; sodecode_out.accept
belongs toencode_in.valid
, andencode_in.accept
belongs todecode_out.valid
. Forencode_out
anddecode_in
, the same applies.
AsciiEscapeLayer
- AsciiEscapeLayer
VHDL counterpart of
stored::AsciiEscapeLayer
.entity AsciiEscapeLayer is generic ( ESCAPE_ALL : boolean := false; ENCODE_OUT_FIFO_DEPTH : natural := 0; DECODE_OUT_FIFO_DEPTH : natural := 0 ); port ( clk : in std_logic; rstn : in std_logic; encode_in : in libstored_pkg.msg_t; encode_out : out libstored_pkg.msg_t; decode_in : in libstored_pkg.msg_t; decode_out : out libstored_pkg.msg_t; idle : out std_logic ); end AsciiEscapeLayer;
- ESCAPE_ALLboolean := false
When
true
, escape all ASCII control characters, otherwise only the subset that conflict with other protocol layers.- ENCODE_OUT_FIFO_DEPTHnatural := 0
The minimum FIFO size to be inserted before
encode_out
. Set to 0 to allow pass-through.- DECODE_OUT_FIFO_DEPTHnatural := 0
The minimum FIFO size to be inserted before
decode_out
. Set to 0 to allow pass-through.
BufferLayer
- BufferLayer
Buffer messages in a FIFO.
entity BufferLayer is generic ( ENCODE_DEPTH : natural := 1; DECODE_DEPTH : natural := 1 ); port ( clk : in std_logic; rstn : in std_logic; encode_in : in libstored_pkg.msg_t := libstored_pkg.msg_term; encode_out : out libstored_pkg.msg_t; decode_in : in libstored_pkg.msg_t := libstored_pkg.msg_term; decode_out : out libstored_pkg.msg_t; idle : out std_logic ); end BufferLayer;
- ENCODE_DEPTHnatural := 1
The minimum FIFO size in the encode path.
- DECODE_DEPTHnatural := 1
The minimum FIFO size in the decode path.
TerminalLayer
- TerminalLayer
VHDL counterpart of
stored::TerminalLayer
.entity TerminalLayer is generic ( ENCODE_OUT_FIFO_DEPTH : natural := 0; DECODE_IN_FIFO_DEPTH : natural := 0; DECODE_OUT_FIFO_DEPTH : natural := 1; TERMINAL_OUT_FIFO_DEPTH : natural := 1 ); port ( clk : in std_logic; rstn : in std_logic; encode_in : in libstored_pkg.msg_t; encode_out : out libstored_pkg.msg_t; decode_in : in libstored_pkg.msg_t; decode_out : out libstored_pkg.msg_t; terminal_in : in libstored_pkg.msg_t := libstored_pkg.msg_term; terminal_out : out libstored_pkg.msg_t; idle : out std_logic ); end TerminalLayer;
- ENCODE_OUT_FIFO_DEPTHnatural := 0
The minimum FIFO size to be inserted before
encode_out
. Set to 0 to allow pass-through.- DECODE_IN_FIFO_DEPTHnatural := 0
The minimum FIFO size to be inserted after
decode_in
. Set to 0 to allow pass-through.- DECODE_OUT_FIFO_DEPTHnatural := 1
The minimum FIFO size to be inserted before
decode_out
. Set to 0 to allow pass-through.- TERMINAL_OUT_FIFO_DEPTHnatural := 1
The minimum FIFO size to be inserted before
terminal_out
. Set to 0 to allow pass-through.
UARTLayer
- UARTLayer
UART end of the protocol stack, with optional RTS/CTS hardware flow control.
entity UARTLayer is generic ( SYSTEM_CLK_FREQ : integer := 100e6; BAUD : integer := 115200; DECODE_OUT_FIFO_DEPTH : natural := 0 ); port ( clk : in std_logic; rstn : in std_logic; encode_in : in libstored_pkg.msg_t; decode_out : out libstored_pkg.msg_t; rx : in std_logic; tx : out std_logic; cts : in std_logic := '0'; rts : out std_logic; idle : out std_logic ); end UARTLayer;
- SYSTEM_CLK_FREQinteger := 100e6
Frequency of
clk
.- BAUDinteger := 115200
Baud rate to be used for
rx
andtx
.- DECODE_OUT_FIFO_DEPTHnatural := 0
The minimum FIFO size to be inserted before
decode_out
.
FileLayer
- FileLayer
File backend of the protocol stack. Only available for simulation.
entity FileLayer is generic ( SLEEP_s : real := 100.0e-6; FILENAME_IN : string := "stack_in.txt"; FILENAME_OUT : string := "stack_out.txt"; VERBOSE : boolean := false ); port ( clk : in std_logic; rstn : in std_logic; encode_in : in libstored_pkg.msg_t; decode_out : out libstored_pkg.msg_t; idle : out std_logic ); end FileLayer;
- SLEEP_sreal := 100.0e-6
Do a
wait for SLEEP_s * 1 sec
when anul
byte is received. This way, the simulation does not block all the time on input data. Some throttling can be implemented this way.- FILENAME_INstring := “stack_in.txt”
File to read from. This can also be a named pipe.
- FILENAME_OUTstring := “stack_out.txt”
File to write to. This can also be a named pipe.
- VERBOSEboolean := false
If
true
, report every read/write to the log.
TeeLayer
- TeeLayer
Write all encoded and/or decoded data to a file, like the UNIX tool
tee
. Only available for simulation.entity TeeLayer is generic ( FILENAME_ENCODE : string := ""; FILENAME_DECODE : string := "" ); port ( clk : in std_logic; rstn : in std_logic; encode_in : in libstored_pkg.msg_t; encode_out : out libstored_pkg.msg_t; decode_in : in libstored_pkg.msg_t; decode_out : out libstored_pkg.msg_t; idle : out std_logic ); end TeeLayer;
- FILENAME_ENCODEstring := “”
File name to write encoded data to. Only used when not empty.
- FILENAME_DECODEstring := “”
File name to write decoded data to. Only used when not empty.
XsimLayer
- XsimLayer
VHDL counterpart of
stored::XsimLayer
. Only available in simulation.entity XsimLayer is generic ( PIPE_PREFIX : string; VERBOSE : boolean := false ); port ( clk : in std_logic; rstn : in std_logic; encode_in : in libstored_pkg.msg_t; decode_out : out libstored_pkg.msg_t; idle : out std_logic ); end XsimLayer;
- PIPE_PREFIXstring
Prefix used for the three named pipes.
- VERBOSEboolean := false
When
true
, show detailed information about all exchanged bytes.
Utilities
libstored_droptail
- libstored_droptail
Drop the tail with a fixed length from a message.
entity libstored_droptail is generic ( FIFO_DEPTH : natural := 0; TAIL_LENGTH : positive := 1 ); port ( clk : in std_logic; rstn : in std_logic; data_in : in std_logic_vector(7 downto 0); valid_in : in std_logic; last_in : in std_logic; accept_in : out std_logic; drop : in std_logic := '1'; data_out : out std_logic_vector(7 downto 0); valid_out : out std_logic; last_out : out std_logic; accept_out : in std_logic ); end libstored_droptail;
Example that drops a tail of length 1:
Note that the precise latency from
data_in
todata_out
is unspecified.- FIFO_DEPTHnatural := 0
Minimum FIFO depth. Set to 0 to allow pass-through.
- TAIL_LENGTHpositive := 1
The tail to be dropped.
- dropin std_logic
This signal is sampled when
last_in
is high. Whendrop
is high, the tail of the message is dropped. So,last_out
is moved a bit back in the stream in that case.
libstored_stream_buffer
- libstored_stream_buffer
Break long combinatorial path by inserting flipflops. This is effectively a FIFO of size 1.
entity libstored_stream_buffer is generic ( WIDTH : positive ); port ( clk : in std_logic; rstn : in std_logic; i : in std_logic_vector(WIDTH - 1 downto 0); i_valid : in std_logic; i_accept : out std_logic; o : out std_logic_vector(WIDTH - 1 downto 0); o_valid : out std_logic; o_accept : in std_logic ); end libstored_stream_buffer;
libstored_fifo
- libstored_fifo
FIFO with optional conditional push, and lookahead before actual pop.
entity libstored_fifo is generic ( WIDTH : positive; DEPTH : natural := 1; ALMOST_FULL_REMAINING : natural := 0; ALMOST_EMPTY_REMAINING : natural := 0 ); port ( clk : in std_logic; rstn : in std_logic; i : in std_logic_vector(WIDTH - 1 downto 0); i_valid : in std_logic; i_accept : out std_logic; i_commit : in std_logic := '1'; i_rollback : in std_logic := '0'; o : out std_logic_vector(WIDTH - 1 downto 0); o_valid : out std_logic; o_accept : in std_logic; o_commit : in std_logic := '1'; o_rollback : in std_logic := '0'; full : out std_logic; empty : out std_logic; almost_full : out std_logic; almost_empty : out std_logic ); end libstored_fifo;
Example to drop and commit pushed data:
Example to drop and commit popped data having a FIFO size of 4:
- WIDTHpositive
Data width.
- DEPTHnatural := 1
Minimum FIFO depth.
- ALMOST_FULL_REMAININGnatural := 0
Set
almost_full
high when at most this number of elements can be pushed in the FIFO without blocking.- ALMOST_EMPTY_REMAININGnatural := 0
Set
almost_empty
high when at most this number of elements can be popped from the FIFO without blocking.- i_commitin std_logic := ‘1’
When high, make the already pushed data aviable for the consumer side. Data that is pushed in the same clock cycle is also committed.
- i_rollbackin std_logic := ‘0’
Drop all previously uncommitted data. Data that is pushed in the same clock cycle is also dropped. If
i_commit
is high in the same cycle, it is ignored, and the data is dropped.- o_commitin std_logic := ‘1’
Remove previously popped data from the FIFO, such that space is made for the producer side to push more.
- o_rollbackin std_logic := ‘0’
Revert previously uncommited popped data, such that this data can be read again.