Implement basic SPI controller

This commit is contained in:
Byron Lathi
2023-07-21 23:01:37 -07:00
parent 85f12c75f1
commit 6a1a76db35
4 changed files with 501 additions and 7 deletions

View File

@@ -6,7 +6,7 @@ all: $(TARGETS)
timer: timer_tb.sv
iverilog -g2005-sv -s sim -o $@ $@_tb.sv ../$@.sv
spi_controller: spi_controller_tb.sv
spi_controller: spi_controller_tb.sv ../spi_controller.sv
iverilog -g2005-sv -s sim -o $@ $@_tb.sv ../$@.sv
interrupt_controller: interrupt_controller_tb.sv

View File

@@ -0,0 +1,448 @@
#! /usr/bin/vvp
:ivl_version "12.0 (stable)";
:ivl_delay_selection "TYPICAL";
:vpi_time_precision - 9;
:vpi_module "/usr/lib64/ivl/system.vpi";
:vpi_module "/usr/lib64/ivl/vhdl_sys.vpi";
:vpi_module "/usr/lib64/ivl/vhdl_textio.vpi";
:vpi_module "/usr/lib64/ivl/v2005_math.vpi";
:vpi_module "/usr/lib64/ivl/va_math.vpi";
:vpi_module "/usr/lib64/ivl/v2009.vpi";
S_0x55defa4f97e0 .scope package, "$unit" "$unit" 2 1;
.timescale 0 0;
S_0x55defa4f9970 .scope module, "sim" "sim" 3 1;
.timescale -8 -9;
v0x55defa5217a0_0 .var "_spi_device_data", 7 0;
v0x55defa5218a0_0 .var "clk_50", 0 0;
v0x55defa521960_0 .var "data", 7 0;
v0x55defa521a20_0 .var "i_addr", 1 0;
v0x55defa521ae0_0 .var "i_clk", 0 0;
v0x55defa521bd0_0 .var "i_cs", 0 0;
v0x55defa521c70_0 .var "i_data", 7 0;
v0x55defa521d10_0 .var "i_rst", 0 0;
v0x55defa521db0_0 .var "i_rwb", 0 0;
v0x55defa521e50_0 .var "i_spi_miso", 0 0;
v0x55defa521f20_0 .net "o_data", 7 0, v0x55defa520590_0; 1 drivers
v0x55defa521ff0_0 .net "o_spi_clk", 0 0, L_0x55defa4e9b80; 1 drivers
v0x55defa5220c0_0 .net "o_spi_cs", 0 0, L_0x55defa4e84f0; 1 drivers
v0x55defa522190_0 .net "o_spi_mosi", 0 0, L_0x55defa4e09c0; 1 drivers
E_0x55defa4f0300 .event edge, v0x55defa520670_0;
S_0x55defa4b6270 .scope module, "dut" "spi_controller" 3 22, 4 1 0, S_0x55defa4f9970;
.timescale 0 0;
.port_info 0 /INPUT 1 "i_clk";
.port_info 1 /INPUT 1 "i_rst";
.port_info 2 /INPUT 1 "i_cs";
.port_info 3 /INPUT 1 "i_rwb";
.port_info 4 /INPUT 2 "i_addr";
.port_info 5 /INPUT 8 "i_data";
.port_info 6 /OUTPUT 8 "o_data";
.port_info 7 /OUTPUT 1 "o_spi_cs";
.port_info 8 /OUTPUT 1 "o_spi_clk";
.port_info 9 /OUTPUT 1 "o_spi_mosi";
.port_info 10 /INPUT 1 "i_spi_miso";
L_0x55defa4e84f0 .functor NOT 1, L_0x55defa522260, C4<0>, C4<0>, C4<0>;
L_0x55defa4e9b80 .functor BUFZ 1, v0x55defa520dd0_0, C4<0>, C4<0>, C4<0>;
L_0x55defa4e09c0 .functor BUFZ 1, v0x55defa520d10_0, C4<0>, C4<0>, C4<0>;
v0x55defa4e8690_0 .net *"_ivl_1", 0 0, L_0x55defa522260; 1 drivers
v0x55defa4e9d20_0 .var "active", 0 0;
v0x55defa4e0b20_0 .var "count", 2 0;
v0x55defa4e11c0_0 .net "i_addr", 1 0, v0x55defa521a20_0; 1 drivers
v0x55defa4e14a0_0 .net "i_clk", 0 0, v0x55defa521ae0_0; 1 drivers
v0x55defa4dfca0_0 .net "i_cs", 0 0, v0x55defa521bd0_0; 1 drivers
v0x55defa4e0660_0 .net "i_data", 7 0, v0x55defa521c70_0; 1 drivers
v0x55defa520350_0 .net "i_rst", 0 0, v0x55defa521d10_0; 1 drivers
v0x55defa520410_0 .net "i_rwb", 0 0, v0x55defa521db0_0; 1 drivers
v0x55defa5204d0_0 .net "i_spi_miso", 0 0, v0x55defa521e50_0; 1 drivers
v0x55defa520590_0 .var "o_data", 7 0;
v0x55defa520670_0 .net "o_spi_clk", 0 0, L_0x55defa4e9b80; alias, 1 drivers
v0x55defa520730_0 .net "o_spi_cs", 0 0, L_0x55defa4e84f0; alias, 1 drivers
v0x55defa5207f0_0 .net "o_spi_mosi", 0 0, L_0x55defa4e09c0; alias, 1 drivers
v0x55defa5208b0_0 .var "r_baud_rate", 7 0;
v0x55defa520990_0 .var "r_clock_counter", 8 0;
v0x55defa520a70_0 .var "r_control", 7 0;
v0x55defa520b50_0 .var "r_input_data", 7 0;
v0x55defa520c30_0 .var "r_output_data", 7 0;
v0x55defa520d10_0 .var "r_spi_mosi", 0 0;
v0x55defa520dd0_0 .var "spi_clk", 0 0;
E_0x55defa4f2fe0/0 .event anyedge, v0x55defa4e11c0_0, v0x55defa5208b0_0, v0x55defa520b50_0, v0x55defa4e9d20_0;
E_0x55defa4f2fe0/1 .event anyedge, v0x55defa520a70_0;
E_0x55defa4f2fe0 .event/or E_0x55defa4f2fe0/0, E_0x55defa4f2fe0/1;
E_0x55defa4f1160 .event negedge, v0x55defa4e14a0_0;
L_0x55defa522260 .part v0x55defa520a70_0, 0, 1;
S_0x55defa521050 .scope task, "read_reg" "read_reg" 3 40, 3 40 0, S_0x55defa4f9970;
.timescale -8 -9;
v0x55defa521220_0 .var "_addr", 2 0;
v0x55defa521320_0 .var "_data", 7 0;
E_0x55defa4da3a0 .event posedge, v0x55defa4e14a0_0;
TD_sim.read_reg ;
%wait E_0x55defa4f1160;
%pushi/vec4 1, 0, 1;
%assign/vec4 v0x55defa521bd0_0, 0;
%load/vec4 v0x55defa521220_0;
%pad/u 2;
%assign/vec4 v0x55defa521a20_0, 0;
%pushi/vec4 1, 0, 1;
%assign/vec4 v0x55defa521db0_0, 0;
%pushi/vec4 255, 0, 8;
%assign/vec4 v0x55defa521c70_0, 0;
%wait E_0x55defa4da3a0;
%load/vec4 v0x55defa521f20_0;
%assign/vec4 v0x55defa521320_0, 0;
%wait E_0x55defa4f1160;
%pushi/vec4 0, 0, 1;
%assign/vec4 v0x55defa521bd0_0, 0;
%pushi/vec4 1, 0, 1;
%assign/vec4 v0x55defa521db0_0, 0;
%end;
S_0x55defa521400 .scope task, "write_reg" "write_reg" 3 27, 3 27 0, S_0x55defa4f9970;
.timescale -8 -9;
v0x55defa5215e0_0 .var "_addr", 2 0;
v0x55defa5216c0_0 .var "_data", 7 0;
TD_sim.write_reg ;
%wait E_0x55defa4f1160;
%pushi/vec4 1, 0, 1;
%assign/vec4 v0x55defa521bd0_0, 0;
%load/vec4 v0x55defa5215e0_0;
%pad/u 2;
%assign/vec4 v0x55defa521a20_0, 0;
%pushi/vec4 0, 0, 1;
%assign/vec4 v0x55defa521db0_0, 0;
%pushi/vec4 255, 0, 8;
%assign/vec4 v0x55defa521c70_0, 0;
%wait E_0x55defa4da3a0;
%load/vec4 v0x55defa5216c0_0;
%assign/vec4 v0x55defa521c70_0, 0;
%wait E_0x55defa4f1160;
%pushi/vec4 0, 0, 1;
%assign/vec4 v0x55defa521bd0_0, 0;
%pushi/vec4 1, 0, 1;
%assign/vec4 v0x55defa521db0_0, 0;
%end;
.scope S_0x55defa4b6270;
T_2 ;
%wait E_0x55defa4f1160;
%load/vec4 v0x55defa520350_0;
%flag_set/vec4 8;
%jmp/0xz T_2.0, 8;
%pushi/vec4 1, 0, 8;
%assign/vec4 v0x55defa5208b0_0, 0;
%pushi/vec4 0, 0, 8;
%assign/vec4 v0x55defa520b50_0, 0;
%pushi/vec4 0, 0, 8;
%assign/vec4 v0x55defa520c30_0, 0;
%pushi/vec4 0, 0, 8;
%assign/vec4 v0x55defa520a70_0, 0;
%pushi/vec4 0, 0, 9;
%assign/vec4 v0x55defa520990_0, 0;
%pushi/vec4 0, 0, 3;
%assign/vec4 v0x55defa4e0b20_0, 0;
%pushi/vec4 0, 0, 1;
%assign/vec4 v0x55defa520dd0_0, 0;
%pushi/vec4 0, 0, 1;
%assign/vec4 v0x55defa4e9d20_0, 0;
%jmp T_2.1;
T_2.0 ;
%load/vec4 v0x55defa520410_0;
%inv;
%load/vec4 v0x55defa4dfca0_0;
%and;
%flag_set/vec4 8;
%jmp/0xz T_2.2, 8;
%load/vec4 v0x55defa4e11c0_0;
%dup/vec4;
%pushi/vec4 0, 0, 2;
%cmp/u;
%jmp/1 T_2.4, 6;
%dup/vec4;
%pushi/vec4 1, 0, 2;
%cmp/u;
%jmp/1 T_2.5, 6;
%dup/vec4;
%pushi/vec4 2, 0, 2;
%cmp/u;
%jmp/1 T_2.6, 6;
%dup/vec4;
%pushi/vec4 3, 0, 2;
%cmp/u;
%jmp/1 T_2.7, 6;
%vpi_call/w 4 52 "$warning", "value is unhandled for priority or unique case statement" {0 0 0};
%jmp T_2.8;
T_2.4 ;
%load/vec4 v0x55defa4e0660_0;
%assign/vec4 v0x55defa5208b0_0, 0;
%jmp T_2.8;
T_2.5 ;
%jmp T_2.8;
T_2.6 ;
%load/vec4 v0x55defa4e0660_0;
%assign/vec4 v0x55defa520c30_0, 0;
%pushi/vec4 1, 0, 1;
%assign/vec4 v0x55defa4e9d20_0, 0;
%jmp T_2.8;
T_2.7 ;
%load/vec4 v0x55defa4e0660_0;
%assign/vec4 v0x55defa520a70_0, 0;
%jmp T_2.8;
T_2.8 ;
%pop/vec4 1;
T_2.2 ;
%load/vec4 v0x55defa4e9d20_0;
%flag_set/vec4 8;
%jmp/0xz T_2.9, 8;
%load/vec4 v0x55defa520c30_0;
%parti/s 1, 7, 4;
%assign/vec4 v0x55defa520d10_0, 0;
%load/vec4 v0x55defa520990_0;
%addi 1, 0, 9;
%assign/vec4 v0x55defa520990_0, 0;
%load/vec4 v0x55defa5208b0_0;
%pad/u 9;
%load/vec4 v0x55defa520990_0;
%cmp/u;
%flag_or 5, 4;
%jmp/0xz T_2.11, 5;
%pushi/vec4 0, 0, 9;
%assign/vec4 v0x55defa520990_0, 0;
%load/vec4 v0x55defa520dd0_0;
%inv;
%assign/vec4 v0x55defa520dd0_0, 0;
%load/vec4 v0x55defa520dd0_0;
%cmpi/e 0, 0, 1;
%jmp/0xz T_2.13, 4;
%load/vec4 v0x55defa520c30_0;
%ix/load 4, 1, 0;
%flag_set/imm 4, 0;
%shiftl 4;
%assign/vec4 v0x55defa520c30_0, 0;
%load/vec4 v0x55defa4e0b20_0;
%addi 1, 0, 3;
%assign/vec4 v0x55defa4e0b20_0, 0;
T_2.13 ;
%load/vec4 v0x55defa520dd0_0;
%cmpi/e 1, 0, 1;
%jmp/0xz T_2.15, 4;
%load/vec4 v0x55defa520b50_0;
%parti/s 7, 0, 2;
%load/vec4 v0x55defa5204d0_0;
%concat/vec4; draw_concat_vec4
%assign/vec4 v0x55defa520b50_0, 0;
%load/vec4 v0x55defa4e0b20_0;
%cmpi/e 0, 0, 3;
%jmp/0xz T_2.17, 4;
%pushi/vec4 0, 0, 1;
%assign/vec4 v0x55defa4e9d20_0, 0;
T_2.17 ;
T_2.15 ;
T_2.11 ;
T_2.9 ;
T_2.1 ;
%jmp T_2;
.thread T_2;
.scope S_0x55defa4b6270;
T_3 ;
Ewait_0 .event/or E_0x55defa4f2fe0, E_0x0;
%wait Ewait_0;
%load/vec4 v0x55defa4e11c0_0;
%dup/vec4;
%pushi/vec4 0, 0, 2;
%cmp/u;
%jmp/1 T_3.0, 6;
%dup/vec4;
%pushi/vec4 1, 0, 2;
%cmp/u;
%jmp/1 T_3.1, 6;
%dup/vec4;
%pushi/vec4 2, 0, 2;
%cmp/u;
%jmp/1 T_3.2, 6;
%dup/vec4;
%pushi/vec4 3, 0, 2;
%cmp/u;
%jmp/1 T_3.3, 6;
%vpi_call/w 4 88 "$warning", "value is unhandled for priority or unique case statement" {0 0 0};
%jmp T_3.4;
T_3.0 ;
%load/vec4 v0x55defa5208b0_0;
%store/vec4 v0x55defa520590_0, 0, 8;
%jmp T_3.4;
T_3.1 ;
%load/vec4 v0x55defa520b50_0;
%store/vec4 v0x55defa520590_0, 0, 8;
%jmp T_3.4;
T_3.2 ;
%jmp T_3.4;
T_3.3 ;
%load/vec4 v0x55defa4e9d20_0;
%load/vec4 v0x55defa520a70_0;
%parti/s 7, 0, 2;
%concat/vec4; draw_concat_vec4
%store/vec4 v0x55defa520590_0, 0, 8;
%jmp T_3.4;
T_3.4 ;
%pop/vec4 1;
%jmp T_3;
.thread T_3, $push;
.scope S_0x55defa4f9970;
T_4 ;
%delay 10, 0;
%load/vec4 v0x55defa5218a0_0;
%pushi/vec4 0, 0, 1;
%cmp/e;
%flag_get/vec4 6;
%store/vec4 v0x55defa5218a0_0, 0, 1;
%jmp T_4;
.thread T_4;
.scope S_0x55defa4f9970;
T_5 ;
%delay 1000, 0;
%load/vec4 v0x55defa521ae0_0;
%pushi/vec4 0, 0, 1;
%cmp/e;
%flag_get/vec4 6;
%store/vec4 v0x55defa521ae0_0, 0, 1;
%jmp T_5;
.thread T_5;
.scope S_0x55defa4f9970;
T_6 ;
%vpi_call/w 3 55 "$dumpfile", "spi_controller.vcd" {0 0 0};
%vpi_call/w 3 56 "$dumpvars", 32'sb00000000000000000000000000000000, S_0x55defa4f9970 {0 0 0};
%end;
.thread T_6;
.scope S_0x55defa4f9970;
T_7 ;
%pushi/vec4 1, 0, 1;
%assign/vec4 v0x55defa521d10_0, 0;
%pushi/vec4 5, 0, 32;
T_7.0 %dup/vec4;
%pushi/vec4 0, 0, 32;
%cmp/s;
%jmp/1xz T_7.1, 5;
%jmp/1 T_7.1, 4;
%pushi/vec4 1, 0, 32;
%sub;
%wait E_0x55defa4da3a0;
%jmp T_7.0;
T_7.1 ;
%pop/vec4 1;
%pushi/vec4 0, 0, 1;
%assign/vec4 v0x55defa521bd0_0, 0;
%pushi/vec4 1, 0, 1;
%assign/vec4 v0x55defa521db0_0, 0;
%pushi/vec4 0, 0, 2;
%assign/vec4 v0x55defa521a20_0, 0;
%pushi/vec4 0, 0, 1;
%assign/vec4 v0x55defa521d10_0, 0;
%pushi/vec4 5, 0, 32;
T_7.2 %dup/vec4;
%pushi/vec4 0, 0, 32;
%cmp/s;
%jmp/1xz T_7.3, 5;
%jmp/1 T_7.3, 4;
%pushi/vec4 1, 0, 32;
%sub;
%wait E_0x55defa4da3a0;
%jmp T_7.2;
T_7.3 ;
%pop/vec4 1;
%pushi/vec4 3, 0, 3;
%store/vec4 v0x55defa5215e0_0, 0, 3;
%pushi/vec4 1, 0, 8;
%store/vec4 v0x55defa5216c0_0, 0, 8;
%fork TD_sim.write_reg, S_0x55defa521400;
%join;
%pushi/vec4 2, 0, 3;
%store/vec4 v0x55defa5215e0_0, 0, 3;
%pushi/vec4 170, 0, 8;
%store/vec4 v0x55defa5216c0_0, 0, 8;
%fork TD_sim.write_reg, S_0x55defa521400;
%join;
%pushi/vec4 128, 0, 8;
%store/vec4 v0x55defa521960_0, 0, 8;
T_7.4 ;
%load/vec4 v0x55defa521960_0;
%pad/u 32;
%pushi/vec4 128, 0, 32;
%and;
%or/r;
%flag_set/vec4 8;
%jmp/0xz T_7.5, 8;
%pushi/vec4 3, 0, 3;
%store/vec4 v0x55defa521220_0, 0, 3;
%fork TD_sim.read_reg, S_0x55defa521050;
%join;
%load/vec4 v0x55defa521320_0;
%store/vec4 v0x55defa521960_0, 0, 8;
%jmp T_7.4;
T_7.5 ;
%pushi/vec4 3, 0, 3;
%store/vec4 v0x55defa5215e0_0, 0, 3;
%pushi/vec4 0, 0, 8;
%store/vec4 v0x55defa5216c0_0, 0, 8;
%fork TD_sim.write_reg, S_0x55defa521400;
%join;
%pushi/vec4 1, 0, 3;
%store/vec4 v0x55defa521220_0, 0, 3;
%fork TD_sim.read_reg, S_0x55defa521050;
%join;
%load/vec4 v0x55defa521320_0;
%store/vec4 v0x55defa521960_0, 0, 8;
%load/vec4 v0x55defa521960_0;
%cmpi/e 85, 0, 8;
%jmp/0xz T_7.6, 4;
%jmp T_7.7;
T_7.6 ;
%vpi_call/w 3 79 "$error" {0 0 0};
T_7.7 ;
%pushi/vec4 50, 0, 32;
T_7.8 %dup/vec4;
%pushi/vec4 0, 0, 32;
%cmp/s;
%jmp/1xz T_7.9, 5;
%jmp/1 T_7.9, 4;
%pushi/vec4 1, 0, 32;
%sub;
%wait E_0x55defa4da3a0;
%jmp T_7.8;
T_7.9 ;
%pop/vec4 1;
%vpi_call/w 3 83 "$finish" {0 0 0};
%end;
.thread T_7;
.scope S_0x55defa4f9970;
T_8 ;
%pushi/vec4 85, 0, 8;
%assign/vec4 v0x55defa5217a0_0, 0;
%end;
.thread T_8;
.scope S_0x55defa4f9970;
T_9 ;
%wait E_0x55defa4f0300;
%load/vec4 v0x55defa5220c0_0;
%cmpi/e 0, 0, 1;
%jmp/0xz T_9.0, 4;
%load/vec4 v0x55defa521ff0_0;
%cmpi/e 1, 0, 1;
%jmp/0xz T_9.2, 4;
%load/vec4 v0x55defa5217a0_0;
%parti/s 1, 7, 4;
%assign/vec4 v0x55defa521e50_0, 0;
T_9.2 ;
%load/vec4 v0x55defa521ff0_0;
%cmpi/e 0, 0, 1;
%jmp/0xz T_9.4, 4;
%load/vec4 v0x55defa5217a0_0;
%ix/load 4, 1, 0;
%flag_set/imm 4, 0;
%shiftl 4;
%assign/vec4 v0x55defa5217a0_0, 0;
T_9.4 ;
T_9.0 ;
%jmp T_9;
.thread T_9;
# The file index is used to find the file name in the following table.
:file_names 5;
"N/A";
"<interactive>";
"-";
"spi_controller_tb.sv";
"../spi_controller.sv";

View File

@@ -56,14 +56,47 @@ begin
$dumpvars(0,sim);
end
logic [7:0] data;
initial begin
i_rst <= '1;
repeat(5) @(posedge i_clk);
i_cs <= '0;
i_rwb <= '1;
i_addr <= '0;
i_rst <= '0;
repeat(5) @(posedge i_clk);
write_reg(3, 1);
write_reg(2, 8'hAA);
data = (1 << 7);
while(data & (1 << 7)) begin
read_reg(3, data);
end
write_reg(3, 0);
read_reg(1, data);
assert(data == 8'h55);
repeat(50) @(posedge i_clk);
$finish();
end
logic [7:0] _spi_device_data;
initial begin
_spi_device_data <= 8'h55;
end
always @(edge o_spi_clk) begin
if (o_spi_cs == '0) begin
if (o_spi_clk == '1)
i_spi_miso <= _spi_device_data[7];
if (o_spi_clk == '0)
_spi_device_data <= _spi_device_data << 1;
end
end
endmodule

View File

@@ -28,18 +28,25 @@ logic [7:0] r_control;
logic [8:0] r_clock_counter;
logic active;
logic [2:0] count;
logic spi_clk;
logic r_spi_mosi;
always @(posedge i_clk) begin
assign o_spi_cs = ~r_control[0];
assign o_spi_clk = spi_clk;
assign o_spi_mosi = r_spi_mosi;
always @(negedge i_clk) begin
if (i_rst) begin
r_baud_rate <= 8'h10;
r_baud_rate <= 8'h1;
r_input_data <= '0;
r_output_data <= '0;
r_control <= '0;
r_clock_counter <= '0;
count <= '0;
spi_clk <= '0;
active <= '0;
end else begin
if (~i_rwb & i_cs) begin
unique case (i_addr)
@@ -54,16 +61,22 @@ always @(posedge i_clk) begin
end
if (active) begin
r_spi_mosi <= r_output_data[0];
r_spi_mosi <= r_output_data[7];
r_clock_counter <= r_clock_counter + 9'b1;
if (r_clock_counter >= r_baud_rate) begin
r_clock_counter <= '0;
spi_clk <= ~spi_clk;
// rising edge
if (spi_clk == '0) begin
r_output_data <= r_output_data >> 1;
r_output_data <= r_output_data << 1;
count <= count + 1;
end
// falling edge
if (spi_clk == '1) begin
r_input_data <= {r_input_data[7:1], i_spi_miso};
r_input_data <= {r_input_data[6:0], i_spi_miso};
if (count == '0) begin
active <= '0;
end
end
end
@@ -76,7 +89,7 @@ always_comb begin
0: o_data = r_baud_rate;
1: o_data = r_input_data;
2:;
3: o_data = r_control;
3: o_data = {active, r_control[6:0]};
endcase
end