From 6f000b64ec96dcdd0176533a62536deddc2d97a6 Mon Sep 17 00:00:00 2001 From: Peter McGoron Date: Wed, 20 Jul 2022 19:41:54 -0400 Subject: [PATCH] start spi master and slave with testbench --- Makefile | 19 +++++ spi_master.v | 126 ++++++++++++++++++++++++++++++++++ spi_slave.v | 95 +++++++++++++++++++++++++ test_spi_write_read_mode0.cpp | 45 ++++++++++++ test_spi_write_read_mode0.v | 58 ++++++++++++++++ 5 files changed, 343 insertions(+) create mode 100644 Makefile create mode 100644 spi_master.v create mode 100644 spi_slave.v create mode 100644 test_spi_write_read_mode0.cpp create mode 100644 test_spi_write_read_mode0.v diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..450ea9a --- /dev/null +++ b/Makefile @@ -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/* diff --git a/spi_master.v b/spi_master.v new file mode 100644 index 0000000..7bb8db1 --- /dev/null +++ b/spi_master.v @@ -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 diff --git a/spi_slave.v b/spi_slave.v new file mode 100644 index 0000000..7fef528 --- /dev/null +++ b/spi_slave.v @@ -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 diff --git a/test_spi_write_read_mode0.cpp b/test_spi_write_read_mode0.cpp new file mode 100644 index 0000000..bbbf03c --- /dev/null +++ b/test_spi_write_read_mode0.cpp @@ -0,0 +1,45 @@ +#include +#include +#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; +} diff --git a/test_spi_write_read_mode0.v b/test_spi_write_read_mode0.v new file mode 100644 index 0000000..56be197 --- /dev/null +++ b/test_spi_write_read_mode0.v @@ -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