Skip to content

Commit

Permalink
wip(verilator): Add verilator testbench utility functions.
Browse files Browse the repository at this point in the history
- Update mkregs.py to generate verilator testbench code
- Add iob_tasks equivalent for verilator
- Update iob-soc verilator testbench to use new functions and drive
  ethernet [WIP]
  • Loading branch information
arturum1 committed Jun 5, 2024
1 parent 82ff8ee commit f90a753
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 72 deletions.
101 changes: 38 additions & 63 deletions hardware/simulation/src/iob_soc_tb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,82 +6,56 @@
#include "verilated.h"
#include "verilated_vcd_c.h"

#include "iob_tasks.h"

#include "iob_soc_conf.h"
#include "iob_uart_swreg.h"

// other macros
#define CLK_PERIOD 1000000000 / FREQ // 1/100MHz*10^9 = 10 ns

vluint64_t main_time = 0;
VerilatedVcdC *tfp = NULL;
Viob_soc_sim_wrapper *dut = NULL;

double sc_time_stamp() { return main_time; }

void Timer(unsigned int ns) {
for (int i = 0; i < ns; i++) {
if (!(main_time % (CLK_PERIOD / 2))) {
dut->clk_i = !(dut->clk_i);
dut->eval();
}
// To add a new clk follow the example
// if(!(main_time%(EXAMPLE_CLK_PERIOD/2))){
// dut->example_clk_in = !(dut->example_clk_in);
//}
main_time += 1;
}
}

// 1-cycle write
void uartwrite(unsigned int cpu_address, unsigned int cpu_data,
unsigned int nbytes) {
char wstrb_int = 0;
switch (nbytes) {
case 1:
wstrb_int = 0b01;
break;
case 2:
wstrb_int = 0b011;
break;
default:
wstrb_int = 0b01111;
break;
}
dut->uart_addr_i = cpu_address;
dut->uart_valid_i = 1;
dut->uart_wstrb_i = wstrb_int << (cpu_address & 0b011);
dut->uart_wdata_i = cpu_data
<< ((cpu_address & 0b011) * 8); // align data to 32 bits
Timer(CLK_PERIOD);
dut->uart_wstrb_i = 0;
dut->uart_valid_i = 0;
}

// 2-cycle read
void uartread(unsigned int cpu_address, char *read_reg) {
dut->uart_addr_i = cpu_address;
dut->uart_valid_i = 1;
Timer(CLK_PERIOD);
*read_reg =
(dut->uart_rdata_o) >> ((cpu_address & 0b011) * 8); // align to 32 bits
dut->uart_valid_i = 0;
}

void inituart() {
void inituart(iob_native_t *uart_if) {
// pulse reset uart
uartwrite(IOB_UART_SOFTRESET_ADDR, 1, IOB_UART_SOFTRESET_W / 8);
uartwrite(IOB_UART_SOFTRESET_ADDR, 0, IOB_UART_SOFTRESET_W / 8);
iob_write(IOB_UART_SOFTRESET_ADDR, 1, IOB_UART_SOFTRESET_W / 8, uart_if);
iob_write(IOB_UART_SOFTRESET_ADDR, 0, IOB_UART_SOFTRESET_W / 8, uart_if);
// config uart div factor
uartwrite(IOB_UART_DIV_ADDR, int(FREQ / BAUD), IOB_UART_DIV_W / 8);
iob_write(IOB_UART_DIV_ADDR, int(FREQ / BAUD), IOB_UART_DIV_W / 8, uart_if);
// enable uart for receiving
uartwrite(IOB_UART_RXEN_ADDR, 1, IOB_UART_RXEN_W / 8);
uartwrite(IOB_UART_TXEN_ADDR, 1, IOB_UART_TXEN_W / 8);
iob_write(IOB_UART_RXEN_ADDR, 1, IOB_UART_RXEN_W / 8, uart_if);
iob_write(IOB_UART_TXEN_ADDR, 1, IOB_UART_TXEN_W / 8, uart_if);
}

int main(int argc, char **argv, char **env) {
Verilated::commandArgs(argc, argv);
Verilated::traceEverOn(true);
dut = new Viob_soc_sim_wrapper;
timer_settings.clk = &dut->clk_i;
timer_settings.eval = &dut->eval;

iob_native_t uart_if = {
&dut->uart_valid_i,
&dut->uart_addr_i,
&dut->uart_wdata_i,
&dut->uart_wstrb_i,
&dut->uart_rdata_o,
&dut->uart_rvalid_o,
&dut->uart_ready_o
}

iob_native_t eth_if = {
&dut->ethernet_valid_i,
&dut->ethernet_addr_i,
&dut->ethernet_wdata_i,
&dut->ethernet_wstrb_i,
&dut->ethernet_rdata_o,
&dut->ethernet_rvalid_o,
&dut->ethernet_ready_o
}

#ifdef VCD
tfp = new VerilatedVcdC;
Expand All @@ -99,9 +73,10 @@ int main(int argc, char **argv, char **env) {
Timer(100);
dut->arst_i = 0;

dut->uart_valid_i = 0;
dut->uart_wstrb_i = 0;
inituart();
*(uart_if.iob_valid) = 0;
*(uart_if.iob_wstrb) = 0;
inituart(&uart_if);
// TODO: Launch parallel ethernet driver

FILE *soc2cnsl_fd;
FILE *cnsl2soc_fd;
Expand All @@ -123,11 +98,11 @@ int main(int argc, char **argv, char **env) {
break;
}
while (!rxread_reg && !txread_reg) {
uartread(IOB_UART_RXREADY_ADDR, &rxread_reg);
uartread(IOB_UART_TXREADY_ADDR, &txread_reg);
rxread_reg = iob_read(IOB_UART_RXREADY_ADDR, &uart_if);
txread_reg = iob_read(IOB_UART_TXREADY_ADDR, &uart_if);
}
if (rxread_reg) {
uartread(IOB_UART_RXDATA_ADDR, &cpu_char);
cpu_char = iob_read(IOB_UART_RXDATA_ADDR, &uart_if);
fwrite(&cpu_char, sizeof(char), 1, soc2cnsl_fd);
fflush(soc2cnsl_fd);
rxread_reg = 0;
Expand All @@ -139,7 +114,7 @@ int main(int argc, char **argv, char **env) {
}
able2write = fread(&cpu_char, sizeof(char), 1, cnsl2soc_fd);
if (able2write > 0) {
uartwrite(IOB_UART_TXDATA_ADDR, cpu_char, IOB_UART_TXDATA_W / 8);
iob_write(IOB_UART_TXDATA_ADDR, cpu_char, IOB_UART_TXDATA_W / 8, &uart_if);
fclose(cnsl2soc_fd);
cnsl2soc_fd = fopen("./cnsl2soc", "w");
}
Expand Down
66 changes: 66 additions & 0 deletions submodules/LIB/hardware/modules/iob_tasks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include "iob_tasks.h"

// Keep track of simulation time
vluint64_t main_time = 0;

// Store timer related settings (clk and eval)
timer_settings_t task_timer_settings;

//
// Verilator functions to drive the IOb Native protocol
//

// Write data to IOb Native slave
// 1-cycle write
void iob_write(unsigned int cpu_address, unsigned int cpu_data,
unsigned int nbytes, iob_native_t *native_if) {
char wstrb_int = 0;
switch (nbytes) {
case 1:
wstrb_int = 0b01;
break;
case 2:
wstrb_int = 0b011;
break;
default:
wstrb_int = 0b01111;
break;
}
*(native_if->iob_addr) = cpu_address;
*(native_if->iob_valid) = 1;
*(native_if->iob_wstrb) = wstrb_int << (cpu_address & 0b011);
*(native_if->iob_wdata) = cpu_data
<< ((cpu_address & 0b011) * 8); // align data to 32 bits
Timer(CLK_PERIOD);
*(native_if->iob_wstrb) = 0;
*(native_if->iob_valid) = 0;
}

// Read data from IOb Native slave
// 2-cycle read
char iob_read(unsigned int cpu_address, iob_native_t *native_if) {
char read_reg = 0;
*(native_if->iob_addr) = cpu_address;
*(native_if->iob_valid) = 1;
Timer(CLK_PERIOD);
read_reg =
*(native_if->iob_rdata) >> ((cpu_address & 0b011) * 8); // align to 32 bits
*(native_if->iob_valid) = 0;
return read_reg;
}

// Delay
void Timer(unsigned int ns) {
for (int i = 0; i < ns; i++) {
if (!(main_time % (CLK_PERIOD / 2))) {
*(task_timer_settings.clk) = !*(task_timer_settings.clk);
(*task_timer_settings.eval)();
}
// To add a new clk follow the example
// if(!(main_time%(EXAMPLE_CLK_PERIOD/2))){
// *(task_timer_settings.example_clk) = !*(task_timer_settings.example_clk);
//}
main_time += 1;
}
}

31 changes: 31 additions & 0 deletions submodules/LIB/hardware/modules/iob_tasks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef H_IOB_TASKS_H
#define H_IOB_TASKS_H

#include "bsp.h"
#include <verilated.h>

#ifndef CLK_PERIOD
#define CLK_PERIOD 1000000000 / FREQ // 1/100MHz*10^9 = 10 ns
#endif

// Struct defining iob-native interface
typedef struct {
unsigned char *iob_valid;
short unsigned int *iob_addr;
unsigned int *iob_wdata;
unsigned char *iob_wstrb;
unsigned int *iob_rdata;
unsigned char *iob_rvalid;
unsigned char *iob_ready;
} iob_native_t;

typedef struct {
unsigned char *clk;
void (*eval)(void);
} timer_settings_t;

void Timer(unsigned int ns);
void iob_write(unsigned int cpu_address, unsigned int cpu_data, unsigned int nbytes, iob_native_t *native_if);
char iob_read(unsigned int cpu_address, iob_native_t *native_if);

#endif // H_IOB_TASKS_H
16 changes: 8 additions & 8 deletions submodules/LIB/hardware/modules/iob_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ class iob_tasks(iob_module):
def _copy_srcs(cls):
out_dir = cls.get_purpose_dir(cls._setup_purpose[-1])
# Copy source to build directory
shutil.copyfile(
os.path.join(cls.setup_dir, "iob_tasks.vs"),
os.path.join(cls.build_dir, out_dir, "iob_tasks.vs"),
)
for src_name in ["iob_tasks.vs", "iob_tasks.cpp", "iob_tasks.h"]:
shutil.copyfile(
os.path.join(cls.setup_dir, src_name),
os.path.join(cls.build_dir, out_dir, src_name),
)

# Ensure sources of other purposes are deleted (except software)
# Check that latest purpose is hardware
if cls._setup_purpose[-1] == "hardware" and len(cls._setup_purpose) > 1:
# Purposes that have been setup previously
for purpose in [x for x in cls._setup_purpose[:-1] if x != "software"]:
# Delete sources for this purpose
os.remove(
os.path.join(
cls.build_dir, cls.PURPOSE_DIRS[purpose], "iob_tasks.v"
for src_name in ["iob_tasks.vs", "iob_tasks.cpp", "iob_tasks.h"]:
os.remove(
os.path.join(cls.build_dir, cls.PURPOSE_DIRS[purpose], src_name)
)
)
2 changes: 1 addition & 1 deletion submodules/LIB/scripts/iob_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ def _generate_hw(cls, mkregs_obj, reg_table):
)
mkregs_obj.write_tbcode(
reg_table,
cls.build_dir + "/hardware/src",
cls.build_dir + "/hardware/simulation/src",
cls.name,
)

Expand Down
Loading

0 comments on commit f90a753

Please sign in to comment.