start spi master and slave with testbench

This commit is contained in:
Peter McGoron 2022-07-20 19:41:54 -04:00
commit 6f000b64ec
5 changed files with 343 additions and 0 deletions

19
Makefile Normal file
View File

@ -0,0 +1,19 @@
TESTBENCH_BASE=test_spi_write_read_mode0
AUXFILES=spi_master.v spi_slave.v
CPP_TESTBENCH=test_spi_write_read_mode0.cpp
WAVEFILE=test_spi_write_read_mode0.vcd
FILES=${TESTBENCH_BASE}.v ${AUXFILES} ${CPP_TESTBENCH}
all: obj_dir/V${TESTBENCH_BASE}
./obj_dir/V${TESTBENCH_BASE} && gtkwave ${WAVEFILE}
obj_dir/V${TESTBENCH_BASE}.mk: ${FILES}
verilator --trace --cc --exe ${FILES} --top ${TESTBENCH_BASE}
obj_dir/V${TESTBENCH_BASE}: obj_dir/V${TESTBENCH_BASE}.mk
make -C obj_dir -f V${TESTBENCH_BASE}.mk
run:
./obj_dir/V${TESTBENCH_CASE}
clean:
$(RM) obj_dir/*

126
spi_master.v Normal file
View File

@ -0,0 +1,126 @@
module spi_master
#(
parameter WID = 24, // Width of bits per transaction.
parameter WID_LEN = 5, // Length in bits required to store WID
parameter CYCLE_HALF_WAIT = 3, // Half of the wait time of a cycle
parameter TIMER_LEN = 3, // Length in bits required to store CYCLE_HALF_WAIT
parameter POLARITY = 0, // 0 = sck idle low, 1 = sck idle high
parameter PHASE = 0 // 0 = rising-read falling-write, 1 = rising-write falling-read.
)
(
input clk,
`ifndef SPI_MASTER_NO_READ
output reg [WID-1:0] from_slave,
input miso,
`endif
`ifndef SPI_MASTER_NO_WRITE
input [WID-1:0] to_slave,
output mosi,
`endif
output sck_wire,
output finished,
input arm
);
parameter WAIT_ON_ARM = 0;
parameter ON_CYCLE = 1;
parameter CYCLE_WAIT = 2;
parameter WAIT_FINISHED = 3;
reg [1:0] state = WAIT_ON_ARM;
reg [WID_LEN-1:0] bit_counter = 0;
reg [TIMER_LEN-1:0] timer = 0;
`ifndef SPI_MASTER_NO_WRITE
reg [WID-1:0] send_buf = 0;
`endif
reg sck = 0;
assign sck_wire = sck;
task idle_state();
if (POLARITY == 0) begin
sck <= 0;
end else begin
sck <= 1;
end
`ifndef SPI_MASTER_NO_WRITE
mosi <= 0;
`endif
timer <= 0;
bit_counter <= 0;
endtask
task read_data();
`ifndef SPI_MASTER_NO_READ
from_slave <= from_slave << 1;
from_slave[0] <= miso;
`endif
endtask
task write_data();
`ifndef SPI_MASTER_NO_WRITE
mosi <= send_buf[WID-1];
send_buf <= send_buf << 1;
`endif
endtask
always @ (posedge clk) begin
case (state)
WAIT_ON_ARM: begin
if (!arm) begin
idle_state();
finished <= 0;
end else begin
state <= ON_CYCLE;
send_buf <= to_slave;
end
end
ON_CYCLE: begin
if (sck) begin // rising edge
if (PHASE == 1) begin
write_data();
end else begin
read_data();
end
if (POLARITY == 1) begin
bit_counter <= bit_counter + 1;
end
end else begin // falling edge
if (PHASE == 1) begin
read_data();
end else begin
write_data();
end
if (POLARITY == 0) begin
bit_counter <= bit_counter + 1;
end
end
state <= CYCLE_WAIT;
end
CYCLE_WAIT: begin
if (timer == CYCLE_HALF_WAIT) begin
timer <= 0;
if (bit_counter == WID) begin
state <= WAIT_FINISHED;
end else begin
state <= ON_CYCLE;
sck <= !sck;
end
end else begin
timer <= timer + 1;
end
end
WAIT_FINISHED: begin
finished <= 1;
idle_state();
if (!arm) begin
state <= WAIT_ON_ARM;
end
end
endcase
end
endmodule

95
spi_slave.v Normal file
View File

@ -0,0 +1,95 @@
module spi_slave
#(
parameter WID = 24, // Width of bits per transaction.
parameter WID_LEN = 5, // Length in bits required to store WID
parameter POLARITY = 0,
parameter PHASE = 0 // 0 = rising-read falling-write, 1 = rising-write falling-read.
)
(
input clk,
input sck,
input ss_L,
`ifndef SPI_SLAVE_NO_READ
output reg [WID-1:0] from_master,
input mosi,
`endif
`ifndef SPI_SLAVE_NO_WRITE
input [WID-1:0] to_master,
output miso,
`endif
output finished,
output err
);
wire ss = !ss_L;
reg sck_delay = 0;
reg [WID_LEN-1:0] bit_counter = 0;
reg ss_delay = 0;
`ifndef SPI_SLAVE_NO_WRITE
reg [WID-1:0] send_buf = 0;
`endif
task read_data();
`ifndef SPI_SLAVE_NO_READ
from_master <= from_master << 1;
from_master[0] <= mosi;
`endif
endtask
task write_data();
`ifndef SPI_SLAVE_NO_WRITE
send_buf <= send_buf << 1;
miso <= send_buf[WID-1];
`endif
endtask
always @ (posedge clk) begin
sck_delay <= sck;
ss_delay <= ss;
case ({ss_delay, ss})
2'b01: begin // rising edge of SS
bit_counter <= 0;
finished <= 0;
err <= 0;
end
2'b10: begin // falling edge
if (bit_counter == WID) begin
finished <= 1;
end else begin
err <= 1;
end
end
2'b11: begin
case ({sck_delay, sck})
2'b01: begin // rising edge
if (PHASE == 1) begin
write_data();
end else begin
read_data();
end
if (POLARITY == 1) begin
bit_counter <= bit_counter + 1;
end
end
2'b10: begin // falling edge
if (PHASE == 1) begin
read_data();
end else begin
write_data();
end
if (POLARITY == 0) begin
bit_counter <= bit_counter + 1;
end
end
default: ;
endcase
end
2'b00: ;
endcase
end
endmodule

View File

@ -0,0 +1,45 @@
#include <stdio.h>
#include <verilated.h>
#include "Vtest_spi_write_read_mode0.h"
using TopModule = Vtest_spi_write_read_mode0;
VerilatedContext *ctx;
TopModule *sim;
static void progress() {
sim->eval();
ctx->timeInc(1);
sim->clk = !sim->clk;
}
static void progress_n(int f) {
for (int i = 0; i < f; i++)
progress();
}
int main(int argc, char **argv) {
ctx = new VerilatedContext;
ctx->traceEverOn(true);
ctx->commandArgs(argc, argv);
sim = new TopModule(ctx);
sim->ss = 0;
sim->clk = 0;
sim->activate = 0;
progress_n(8);
sim->ss = 1;
progress();
sim->data_ctrl = 0b110011011111001100011111;
sim->activate = 1;
while (!sim->master_finished)
progress();
progress_n(5);
sim->ss = 0;
progress_n(5);
sim->final();
delete sim;
return 0;
}

View File

@ -0,0 +1,58 @@
module test_spi_write_read_mode0
(
input clk,
input [23:0] data_ctrl,
input activate,
input ss,
output master_finished,
output slave_finished,
output slave_error
);
wire miso;
wire mosi;
wire sck;
wire ss_L = !ss;
reg [23:0] from_slave_data;
spi_master master
(
.clk(clk),
.to_slave(data_ctrl),
.from_slave(from_slave_data),
.miso(miso),
.mosi(mosi),
.sck_wire(sck),
.finished(master_finished),
.arm(activate)
);
reg [23:0] data_from_master;
reg [23:0] data_to_master = 24'b111011011100010101010101;
spi_slave spi_slave
(
.clk(clk),
.sck(sck),
.ss_L(ss_L),
.from_master(data_from_master),
.to_master(data_to_master),
.mosi(mosi),
.miso(miso),
.finished(slave_finished),
.err(slave_error)
);
always @ (posedge clk) begin
if (slave_finished) begin
data_to_master <= data_from_master;
end
end
initial begin
$dumpfile("test_spi_write_read_mode0.vcd");
$dumpvars();
end
endmodule