From 0907a76c22b2d5d55c86e04a01306a84bf4dbc6e Mon Sep 17 00:00:00 2001 From: Peter McGoron Date: Mon, 14 Nov 2022 08:43:16 -0500 Subject: [PATCH] import spi v0.2 --- firmware/rtl/spi/spi_master.v | 68 ++++++++++---- firmware/rtl/spi/spi_master_ss.v | 4 + firmware/rtl/spi/spi_master_ss_no_read.v | 5 + firmware/rtl/spi/spi_master_ss_no_write.v | 5 + firmware/rtl/spi/spi_master_ss_template.v | 108 ++++++++++++++++++++++ firmware/rtl/spi/spi_slave.v | 19 +++- firmware/rtl/spi/spi_slave_no_read.v | 3 + 7 files changed, 190 insertions(+), 22 deletions(-) create mode 100644 firmware/rtl/spi/spi_master_ss.v create mode 100644 firmware/rtl/spi/spi_master_ss_no_read.v create mode 100644 firmware/rtl/spi/spi_master_ss_no_write.v create mode 100644 firmware/rtl/spi/spi_master_ss_template.v create mode 100644 firmware/rtl/spi/spi_slave_no_read.v diff --git a/firmware/rtl/spi/spi_master.v b/firmware/rtl/spi/spi_master.v index 9169111..0f07d8a 100644 --- a/firmware/rtl/spi/spi_master.v +++ b/firmware/rtl/spi/spi_master.v @@ -1,5 +1,12 @@ -/* SPI master. - * Written by Peter McGoron, 2022. +/* (c) Peter McGoron 2022 v0.2 + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v.2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +/* CYCLE_HALF_WAIT should take into account the setup time of the slave + * device, and also master buffering (MISO is one cycle off to stabilize + * the input). */ module @@ -12,11 +19,11 @@ spi_master_no_write spi_master `endif `endif - #( parameter WID = 24, // Width of bits per transaction. parameter WID_LEN = 5, // Length in bits required to store WID - parameter CYCLE_HALF_WAIT = 1, // Half of the wait time of a cycle + parameter CYCLE_HALF_WAIT = 1, // Half of the wait time of a cycle minus 1. + // One SCK cycle is 2*(CYCLE_HALF_WAIT + 1) clock cycles. 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. @@ -29,13 +36,30 @@ spi_master `endif `ifndef SPI_MASTER_NO_WRITE input [WID-1:0] to_slave, - output mosi, + output reg mosi, `endif - output sck_wire, - output finished, + output reg sck_wire, + output reg finished, input arm ); +`ifndef SPI_MASTER_NO_READ +/* MISO is almost always an external wire, so buffer it. + * This might not be necessary, since the master and slave do not respond + * immediately to changes in the wires, but this is just to be safe. + * It is trivial to change, just do + * wire read_miso = miso; + */ + +reg miso_hot = 0; +reg read_miso = 0; + +always @ (posedge clk) begin + read_miso <= miso_hot; + miso_hot <= miso; +end +`endif + parameter WAIT_ON_ARM = 0; parameter ON_CYCLE = 1; parameter CYCLE_WAIT = 2; @@ -68,7 +92,7 @@ endtask task read_data(); `ifndef SPI_MASTER_NO_READ from_slave <= from_slave << 1; - from_slave[0] <= miso; + from_slave[0] <= read_miso; `endif endtask @@ -100,6 +124,16 @@ task setup_bits(); end endtask +task cycle_change(); + // Stop transfer when the clock returns to its original polarity. + if (bit_counter == WID[WID_LEN-1:0] && sck == POLARITY[0]) begin + state <= WAIT_FINISHED; + end else begin + sck <= !sck; + state <= ON_CYCLE; + end +endtask + always @ (posedge clk) begin case (state) WAIT_ON_ARM: begin @@ -132,19 +166,17 @@ always @ (posedge clk) begin bit_counter <= bit_counter + 1; end end - state <= CYCLE_WAIT; + + if (CYCLE_HALF_WAIT == 0) begin + cycle_change(); + end else begin + state <= CYCLE_WAIT; + end end CYCLE_WAIT: begin if (timer == CYCLE_HALF_WAIT) begin - timer <= 0; - // Stop transfer when the clock returns - // to its original polarity. - if (bit_counter == WID && sck == POLARITY) begin - state <= WAIT_FINISHED; - end else begin - state <= ON_CYCLE; - sck <= !sck; - end + timer <= 1; + cycle_change(); end else begin timer <= timer + 1; end diff --git a/firmware/rtl/spi/spi_master_ss.v b/firmware/rtl/spi/spi_master_ss.v new file mode 100644 index 0000000..6dc3429 --- /dev/null +++ b/firmware/rtl/spi/spi_master_ss.v @@ -0,0 +1,4 @@ +`define SPI_MASTER_SS_NAME spi_master_ss +`define SPI_MASTER_NAME spi_master +/* verilator lint_off DECLFILENAME */ +`include "spi_master_ss_template.v" diff --git a/firmware/rtl/spi/spi_master_ss_no_read.v b/firmware/rtl/spi/spi_master_ss_no_read.v new file mode 100644 index 0000000..741e940 --- /dev/null +++ b/firmware/rtl/spi/spi_master_ss_no_read.v @@ -0,0 +1,5 @@ +`define SPI_MASTER_SS_NAME spi_master_ss_no_read +`define SPI_MASTER_NAME spi_master_no_read +`define SPI_MASTER_NO_READ +/* verilator lint_off DECLFILENAME */ +`include "spi_master_ss_template.v" diff --git a/firmware/rtl/spi/spi_master_ss_no_write.v b/firmware/rtl/spi/spi_master_ss_no_write.v new file mode 100644 index 0000000..9be3e7f --- /dev/null +++ b/firmware/rtl/spi/spi_master_ss_no_write.v @@ -0,0 +1,5 @@ +`define SPI_MASTER_SS_NAME spi_master_ss_no_write +`define SPI_MASTER_NAME spi_master_no_write +`define SPI_MASTER_NO_WRITE +/* verilator lint_off DECLFILENAME */ +`include "spi_master_ss_template.v" diff --git a/firmware/rtl/spi/spi_master_ss_template.v b/firmware/rtl/spi/spi_master_ss_template.v new file mode 100644 index 0000000..e2e0cc4 --- /dev/null +++ b/firmware/rtl/spi/spi_master_ss_template.v @@ -0,0 +1,108 @@ +/* spi master with integrated ability to wait a certain amount of cycles + * after activating SS. + */ + +module `SPI_MASTER_SS_NAME +#( + parameter WID = 24, + parameter WID_LEN = 5, + parameter CYCLE_HALF_WAIT = 1, + parameter TIMER_LEN = 3, + + parameter SS_WAIT = 1, + parameter SS_WAIT_TIMER_LEN = 2, + + parameter POLARITY = 0, + parameter PHASE = 0 +) +( + input clk, +`ifndef SPI_MASTER_NO_READ + output [WID-1:0] from_slave, + input miso, +`endif +`ifndef SPI_MASTER_NO_WRITE + input [WID-1:0] to_slave, + output reg mosi, +`endif + output sck_wire, + output finished, + output ss_L, + input arm +); + +reg ss = 0; +reg arm_master = 0; +assign ss_L = !ss; + +`SPI_MASTER_NAME #( + .WID(WID), + .WID_LEN(WID_LEN), + .CYCLE_HALF_WAIT(CYCLE_HALF_WAIT), + .TIMER_LEN(TIMER_LEN), + .POLARITY(POLARITY), + .PHASE(PHASE) +) master ( + .clk(clk), +`ifndef SPI_MASTER_NO_READ + .from_slave(from_slave), + .miso(miso), +`endif +`ifndef SPI_MASTER_NO_WRITE + .to_slave(to_slave), + .mosi(mosi), +`endif + .sck_wire(sck_wire), + .finished(finished), + .arm(arm_master) +); + +localparam WAIT_ON_ARM = 0; +localparam WAIT_ON_SS = 1; +localparam WAIT_ON_MASTER = 2; +localparam WAIT_ON_ARM_DEASSERT = 3; +reg [2:0] state = WAIT_ON_ARM; +reg [SS_WAIT_TIMER_LEN-1:0] timer = 0; + +task master_arm(); + arm_master <= 1; + state <= WAIT_ON_MASTER; +endtask + +always @ (posedge clk) begin + case (state) + WAIT_ON_ARM: begin + if (arm) begin + timer <= 1; + if (SS_WAIT == 0) begin + master_arm(); + end else begin + timer <= 1; + state <= WAIT_ON_SS; + end + ss <= 1; + end + end + WAIT_ON_SS: begin + if (timer == SS_WAIT) begin + master_arm(); + end else begin + timer <= timer + 1; + end + end + WAIT_ON_MASTER: begin + if (finished) begin + state <= WAIT_ON_ARM_DEASSERT; + ss <= 0; + end + end + WAIT_ON_ARM_DEASSERT: begin + if (!arm) begin + state <= WAIT_ON_ARM; + arm_master <= 0; + end + end + endcase +end + +endmodule diff --git a/firmware/rtl/spi/spi_slave.v b/firmware/rtl/spi/spi_slave.v index 6efacdd..c4f7a7c 100644 --- a/firmware/rtl/spi/spi_slave.v +++ b/firmware/rtl/spi/spi_slave.v @@ -1,4 +1,4 @@ -/* (c) Peter McGoron 2022 v0.1 +/* (c) Peter McGoron 2022 v0.2 * This Source Code Form is subject to the terms of the Mozilla Public * License, v.2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. @@ -24,7 +24,7 @@ spi_slave input ss_L, `ifndef SPI_SLAVE_NO_READ output reg [WID-1:0] from_master, - input reg mosi, + input mosi, `endif `ifndef SPI_SLAVE_NO_WRITE input [WID-1:0] to_master, @@ -35,6 +35,17 @@ spi_slave output reg err ); +`ifndef SPI_SLAVE_NO_READ +/* MOSI is almost always an external wire, so buffer it. */ +reg mosi_hot = 0; +reg read_mosi = 0; + +always @ (posedge clk) begin + read_mosi <= mosi_hot; + mosi_hot <= mosi; +end +`endif + wire ss = !ss_L; reg sck_delay = 0; reg [WID_LEN-1:0] bit_counter = 0; @@ -48,7 +59,7 @@ reg [WID-1:0] send_buf = 0; task read_data(); `ifndef SPI_SLAVE_NO_READ from_master <= from_master << 1; - from_master[0] <= mosi; + from_master[0] <= read_mosi; `endif endtask @@ -77,7 +88,7 @@ task setup_bits(); endtask task check_counter(); - if (bit_counter == WID) begin + if (bit_counter == WID[WID_LEN-1:0]) begin err <= ready_at_start; end else begin bit_counter <= bit_counter + 1; diff --git a/firmware/rtl/spi/spi_slave_no_read.v b/firmware/rtl/spi/spi_slave_no_read.v new file mode 100644 index 0000000..12705b9 --- /dev/null +++ b/firmware/rtl/spi/spi_slave_no_read.v @@ -0,0 +1,3 @@ +`define SPI_SLAVE_NO_READ +/* verilator lint_off DECLFILENAME */ +`include "spi_slave.v"