5. Hardware Interfaces
5.1. Hardware Interfaces
5.1.1. SLAVE_CONTROLLER_INTERFACE
The SLAVE_CONTROLLER
module facilitates communication between the host CPU and multiple engine cores through 6 registers.
These registers are used to send commands to the engine cores, with a specific format for handling operations and context data.
The lgoic signals and registers are influenced by the following generics:
Name |
Type |
Default |
Description |
---|---|---|---|
|
|
|
Specifies the width of the data bus, which is the size of each register. |
|
|
|
Defines the number of engine cores connected to the SLAVE_CONTROLLER. |
The following ports allow communication with the engine cores:
Signal Name |
Direction |
Width |
Description |
---|---|---|---|
|
|
|
Register used for sending operation codes (op-codes). |
|
|
|
Register used for context-specific information. |
|
|
|
Register used for context-specific information. |
|
|
|
Register used for context-specific information. |
|
|
|
Register used for context-specific information. |
|
|
|
Register used for context-specific information. |
Signals for communication status:
Signal Name |
Direction |
Width |
Description |
---|---|---|---|
|
|
|
Indicates a new command for the cores. |
|
|
|
Signals when a core has completed executing a command. |
The SLAVE_CONTROLLER
targets more than only one engine core. The number is determined
by the generic NUMBER_OF_CORES
. Each bit of c_new_command
and c_command_complete
corresponds to one engine core.
5.1.2. MASTER_CONTROLLER_INTEFACE
The MASTER_CONTROLLER
module interfaces between multiple engine cores and external DDR memory,
handling simultaneous memory transaction requests (read and write) from different cores. It
prioritizes requests based on two main rules:
Core priority: The core with the lowest index has the highest priority.
Request priority: Write requests are prioritized over read requests within a core.
The module has three configurable parameters to adjust for various system requirements:
Name |
Type |
Default |
Description |
---|---|---|---|
|
|
|
Specifies the width of the data bus between the cores and the memory. |
|
|
|
Defines the address width of the memory being accessed. |
|
|
|
Determines the number of cores that will interface with the memory controller. |
The following ports connect to the engine cores, enabling read and write transactions:
Signal Name |
Direction |
Width |
Description |
---|---|---|---|
|
|
|
Read request signals from each core. |
|
|
|
Write request signals from each core. |
|
|
|
Read addresses from each core. |
|
|
|
Write addresses from each core. |
|
|
|
Data output from the core to the memory. |
|
|
|
Data input from the memory to each core. |
|
|
|
Transfer byte lengths for write operations for each core. |
|
|
|
Transfer byte lengths for read operations for each core. |
|
|
|
Indicates when a core should provide the next word for a write operation. |
|
|
|
Indicates when memory should provide the next word for a read operation. |
|
|
|
Indicates completion of the current transaction. |
Every core takes a specific portion of the signals. Each engine core connected to a port with the standard logic vector ..
(NUMBER_OF_CORES - 1 downto 0) takes 1 bit.
(NUMBER_OF_CORES * AXI_ADDR_WIDTH - 1 downto 0) takes AXI_ADDR_WIDTH bits.
(NUMBER_OF_CORES * AXI_DATA_WIDTH - 1 downto 0) takes AXI_DATA_WIDTH bits.
The transaction has essentially 3 states
IDLE: The
MASTER_CONTROLLER
waits for a read or write requestSTART_TXN: The
MASTER_CONTROLLER
prioritizes the engine core and write over read requestsTXN: The
MASTER_CONTROLLER
performs the transaction
For the engine core the following aspects need to be taken care of
Sart a write request: Set the following singals until the write request has started
set
c_wr_req
to ‘1’ to indicate a write requestset
c_wxfer_length
which is the number of bytes to be send to the memory. It ranges from 4 * 2^3 to 4 * 2^15, where 4 is the 4 bytes of a 32 bit word.set
waddr
which is the write target address of the external memoryset
in_data
which is the first data word to send to external memory
Write transaction:
wait until the core asks for new data when c_next_wdata
is ‘1’. This means the MASTER_CONTROLLER
has accepted the request and starts the transaction
c_wxfer_length
is irrelevantwaddr
is irrelevantc_wr_req
can be set to ‘0’ to not start a new request after the transaction has finishedc_next_wdata
on high indicates whenever in the next clock cycle a new word is written. Make sure to provide the next data word atin_data
c_done
on high indicates that a write or read transaction has been completed. Sincec_next_wdata
was triggered before you know its a write transaction.
Sart a read request: Set the following singals until the read request has started
set
c_rd_req
to ‘1’ to indicate a read requestset
c_rxfer_length
which is the number of bytes to be send to the memory. It ranges from 4 * 2^3 to 4* 2^15, where 4 is the 4 bytes of a 32 bit word.set
waddr
which is the read target address of the external memory
Read transaction:
wait until the core asks for new data when c_next_wdata
is ‘1’. This means the MASTER_CONTROLLER
has accepted the request and starts the transaction
c_wxfer_length
is irrelevantwaddr
is irrelevantc_wr_req
can be set to ‘0’ to not start a new request after the transaction has finishedc_next_wdata
on high indicates whenever in the next clock cycle a new word is written. Make sure to provide the next data word atin_data
c_done
on high indicates that a write or read transaction has been completed. Sincec_next_wdata
was triggered before you know its a write transaction.
A diagramm demonstrating a write request
A diagramm demonstrating a read request
5.2. Software Interfaces
5.2.1. SLAVE_CONTROLLER_INTERFACE
Send a command to an engine core:
The following code examples how the SLAVE_CONTROLLER receives a new command.
Besides the command send logic, there are 6 more register that provide command context.
For example context look into the AXI_TEST_ENGINE_CORE_INTERFACE
.
Here the following two signals matter:
control_command_reg_0_in
(reg_data[0]) : A write only register Each bit belongs to one engine core. Be aware that there are often more bits provided than engine cores are connected. When the bit targeting the engine core is set to ‘1’ the corresponding engine core receives a new command. Make sure to set the bit afterwards to 0. A new command can only be send if the bit has been 0 before.control_status_1_out
(reg_data[1]): A read only register Each bit belongs to one engine core. Be aware that there are often more bits provided than engine cores are connected. TheSLAVE_CONTROLLER
sets a bit to ‘1’ after a command has been send to the corresponding engine core. It indicates that the engine core is busy and wont process new requests
In vhdl:
-- <<Here is place to set the other register for command context>>
-- Send a command to the first engine core with the index 0
control_command_reg_0_in(0) <= '1';
-- Wait for a clock cycle using the THANNA_TEST_UTILS package
run_cycle(clk);
-- Set the send command bit for the engine core 0 back to 0
control_command_reg_0_in(0) <= '0';
-- Wait until the status register at index 0 is '0' which indicates that
-- the engine core has completed the command and is not busy anymore
while control_status_1_out(0) = '0' loop run_cycle(clk); end loop;
In C:
// <<Here is place to set the other register for command context>>
// Send a command to the first engine core with the index 0
reg_data[0] = 1;
// Set the send command bit for the engine core 0 back to 0
reg_data[0] = 0;
// Wait until the status register at index 0 is '0' which indicates that
// the engine core has completed the command and is not busy anymore
printf("WAIT: \n\r");
while ((reg_data[1] & 1) != 1)
{ // Looping = waiting
for(size_t i = 0; i < 1000000; i++);
}
printf("Read complete");
5.2.2. AXI_TEST_ENGINE_CORE_INTERFACE
The following examples show how to provide the command context for the AXI_TEST
engine core.
To send the command append the send command logic from the SLAVE_CONTROLLER_INTERFACE
.
The following AXI_DATA_WIDTH
generic defines the size of the register, which depend on the devices
data bus. In the case of the Zybo Z-720 it is 32 Bit. In the case of AXI_TEST
the register are used
as follows:
Register |
Write/Read |
Width |
Description |
---|---|---|---|
|
|
|
Register used for sending operation codes (op-codes). |
|
|
|
Register containing the write or read address for a memory transaction. |
|
|
|
Start index of the burst register in the AXI_TEST module. Only relevant for memory transactions. |
|
|
|
Number of burst bytes transferred in a memory transaction. It can range from 4 * 2^2 to 4 * 2^15. |
Read command:
In vhdl:
head_reg_2_in <= std_logic_vector(to_unsigned(2, AXI_DATA_WIDTH)); -- Op code for the read command
payload_3_in <= (others => '0'); -- Address initialized with a pointer pointing to free space in the DDR
payload_5_in <= (others => '0'); -- Start index of the burst register in the AXI_TEST module
payload_6_in <= std_logic_vector(to_unsigned(4 * 8, AXI_DATA_WIDTH)); -- Number of burst bytes. It can range from 4 * 2^2 to 4 * 2^15
In C:
reg_data[2] = 2; // Op code for the read command
reg_data[3] = (uint32_t)ddr_data; // Address initialized with a pointer pointing to free space in the DDR
reg_data[5] = 0; // Start index of the burst register in the AXI_TEST module
reg_data[6] = 8; // Number of burst bytes. It can range from 4 * 2^2 to 4 * 2^15
Write command:
In vhdl:
head_reg_2_in <= std_logic_vector(to_unsigned(1, AXI_DATA_WIDTH)); -- Op code for the write command
payload_3_in <= (others => '0'); -- Address initialized with a pointer pointing to free space in the DDR
payload_5_in <= (others => '0'); -- Start index of the burst register in the AXI_TEST module
payload_6_in <= std_logic_vector(to_unsigned(4 * 8, AXI_DATA_WIDTH)); -- Number of burst bytes. It can range from 4 * 2^2 to 4 * 2^15.
In C:
reg_data[2] = 1; // Op code for the write command
reg_data[3] = (uint32_t)ddr_data; // Address initialized with a pointer pointing to free space in the DDR
reg_data[5] = 0; // Start index of the burst register in the AXI_TEST module
reg_data[6] = 8; // Number of burst bytes. It can range from 4 * 2^2 to 4 * 2^15.
Increment command:
In vhdl:
head_reg_2_in <= std_logic_vector(to_unsigned(16, AXI_DATA_WIDTH)); -- Op code for the increment command
In C:
reg_data[2] = 16; // Op code for the increment command