import spi v0.2
This commit is contained in:
parent
50ea679e02
commit
0907a76c22
|
@ -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
|
||||||
state <= CYCLE_WAIT;
|
|
||||||
|
if (CYCLE_HALF_WAIT == 0) begin
|
||||||
|
cycle_change();
|
||||||
|
end else begin
|
||||||
|
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
|
||||||
|
|
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
|
@ -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
|
|
@ -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;
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
`define SPI_SLAVE_NO_READ
|
||||||
|
/* verilator lint_off DECLFILENAME */
|
||||||
|
`include "spi_slave.v"
|
Loading…
Reference in New Issue