import spi v0.2

This commit is contained in:
Peter McGoron 2022-11-14 08:43:16 -05:00
parent 50ea679e02
commit 0907a76c22
7 changed files with 190 additions and 22 deletions

View File

@ -1,5 +1,12 @@
/* SPI master. /* (c) Peter McGoron 2022 v0.2
* Written by Peter McGoron, 2022. * 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 module
@ -12,11 +19,11 @@ spi_master_no_write
spi_master spi_master
`endif `endif
`endif `endif
#( #(
parameter WID = 24, // Width of bits per transaction. parameter WID = 24, // Width of bits per transaction.
parameter WID_LEN = 5, // Length in bits required to store WID 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 TIMER_LEN = 3, // Length in bits required to store CYCLE_HALF_WAIT
parameter POLARITY = 0, // 0 = sck idle low, 1 = sck idle high parameter POLARITY = 0, // 0 = sck idle low, 1 = sck idle high
parameter PHASE = 0 // 0 = rising-read falling-write, 1 = rising-write falling-read. parameter PHASE = 0 // 0 = rising-read falling-write, 1 = rising-write falling-read.
@ -29,13 +36,30 @@ spi_master
`endif `endif
`ifndef SPI_MASTER_NO_WRITE `ifndef SPI_MASTER_NO_WRITE
input [WID-1:0] to_slave, input [WID-1:0] to_slave,
output mosi, output reg mosi,
`endif `endif
output sck_wire, output reg sck_wire,
output finished, output reg finished,
input arm 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 WAIT_ON_ARM = 0;
parameter ON_CYCLE = 1; parameter ON_CYCLE = 1;
parameter CYCLE_WAIT = 2; parameter CYCLE_WAIT = 2;
@ -68,7 +92,7 @@ endtask
task read_data(); task read_data();
`ifndef SPI_MASTER_NO_READ `ifndef SPI_MASTER_NO_READ
from_slave <= from_slave << 1; from_slave <= from_slave << 1;
from_slave[0] <= miso; from_slave[0] <= read_miso;
`endif `endif
endtask endtask
@ -100,6 +124,16 @@ task setup_bits();
end end
endtask 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 always @ (posedge clk) begin
case (state) case (state)
WAIT_ON_ARM: begin WAIT_ON_ARM: begin
@ -132,19 +166,17 @@ always @ (posedge clk) begin
bit_counter <= bit_counter + 1; bit_counter <= bit_counter + 1;
end end
end end
if (CYCLE_HALF_WAIT == 0) begin
cycle_change();
end else begin
state <= CYCLE_WAIT; state <= CYCLE_WAIT;
end end
end
CYCLE_WAIT: begin CYCLE_WAIT: begin
if (timer == CYCLE_HALF_WAIT) begin if (timer == CYCLE_HALF_WAIT) begin
timer <= 0; timer <= 1;
// Stop transfer when the clock returns cycle_change();
// to its original polarity.
if (bit_counter == WID && sck == POLARITY) begin
state <= WAIT_FINISHED;
end else begin
state <= ON_CYCLE;
sck <= !sck;
end
end else begin end else begin
timer <= timer + 1; timer <= timer + 1;
end end

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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 * 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 * 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/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
@ -24,7 +24,7 @@ spi_slave
input ss_L, input ss_L,
`ifndef SPI_SLAVE_NO_READ `ifndef SPI_SLAVE_NO_READ
output reg [WID-1:0] from_master, output reg [WID-1:0] from_master,
input reg mosi, input mosi,
`endif `endif
`ifndef SPI_SLAVE_NO_WRITE `ifndef SPI_SLAVE_NO_WRITE
input [WID-1:0] to_master, input [WID-1:0] to_master,
@ -35,6 +35,17 @@ spi_slave
output reg err 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; wire ss = !ss_L;
reg sck_delay = 0; reg sck_delay = 0;
reg [WID_LEN-1:0] bit_counter = 0; reg [WID_LEN-1:0] bit_counter = 0;
@ -48,7 +59,7 @@ reg [WID-1:0] send_buf = 0;
task read_data(); task read_data();
`ifndef SPI_SLAVE_NO_READ `ifndef SPI_SLAVE_NO_READ
from_master <= from_master << 1; from_master <= from_master << 1;
from_master[0] <= mosi; from_master[0] <= read_mosi;
`endif `endif
endtask endtask
@ -77,7 +88,7 @@ task setup_bits();
endtask endtask
task check_counter(); task check_counter();
if (bit_counter == WID) begin if (bit_counter == WID[WID_LEN-1:0]) begin
err <= ready_at_start; err <= ready_at_start;
end else begin end else begin
bit_counter <= bit_counter + 1; bit_counter <= bit_counter + 1;

View File

@ -0,0 +1,3 @@
`define SPI_SLAVE_NO_READ
/* verilator lint_off DECLFILENAME */
`include "spi_slave.v"