import Vivado project, rearrange Verilog sources

This commit is contained in:
slederer 2024-09-27 22:13:23 +02:00
parent 18b95b6bb6
commit a441e7e042
29 changed files with 789 additions and 742 deletions

View file

@ -0,0 +1,372 @@
`timescale 1ns / 1ps
// Every spi_clk_div cpu clock cycles the spi clock line is inverted.
// So a spi clock cycle is 2*spi_clk_div cpu clock cycles.
// spi_clk_count counts the cpu cycles from spi_clk_div down to zero.
// The resulting spi clock frequency is:
// (cpu clock freq) / ((sclk_count + 1) * 2)
// So for a 83.33 MHz cpu clock, we get
// spi_clk_div = 10: 83.333 / 22 = 3.788 MHz
// spi_clk_div = 124: 83.333 / 250 = 333.33 KHz
module sdspi(
input wire clk, // bus clock
input wire reset,
input wire[7:0] tx_data, // data to transmit
output wire[7:0] rx_data, // data received
output wire tx_ready, // ready to write a data byte
output wire tx_empty, // transmitter fifo is empty
output wire rx_avail, // a byte has been received
output wire rx_ovr, // receiver overrun
input wire tx_write, // write strobe
input wire rx_read, // read strobe (clears rx_ovr)
output wire card_detect, // true is card is present
output wire card_changed, // card_detect signal has changed
output wire card_busy, // card is busy (MISO/DO is 0)
input wire ctrl_write, // set the following flags
input wire rx_filter_en, // set to discard received $FF bytes
input wire txrx_en, // enable transmitter and receiver
input wire spiclk_f_en, // enable spi clock without cs
input wire spiclk_div_wr, // set clock divider via tx_data
// PMOD connections
output wire sd_cs_n,
output reg sd_mosi,
input wire sd_miso,
output wire sd_sck,
input wire sd_cd
);
localparam CLKPHASE_0A = 2'b00;
localparam CLKPHASE_0B = 2'b01;
localparam CLKPHASE_1A = 2'b10;
localparam CLKPHASE_1B = 2'b11;
reg [1:0] clk_phase;
reg xcvr_on; // if turned off, the rest of the current byte
// will still transmitted, and until then "running"
// will be 1
(* KEEP *)
reg running; // transmitting/receiving a byte (maybe a dummy byte)
(* KEEP *) reg [3:0] xcvr_bitcount; // number of bits left of the current byte
reg [7:0] tx_shifter;
wire tx_fifo_wr_en;
reg tx_fifo_rd_en;
wire tx_fifo_full;
wire tx_fifo_empty;
wire [7:0] tx_fifo_out;
reg [7:0] rx_shifter;
reg rx_filter;
reg rx_fifo_wr_en;
wire rx_fifo_rd_en;
wire rx_fifo_full;
wire rx_fifo_empty;
wire [7:0] rx_fifo_out;
reg rx_bit_recvd; // this flag signals a received bit
reg rx_overrun; // byte received when rx fifo is full
reg c_changed;
reg c_cs;
reg spi_clk; // the spi clock signal
reg spi_clk_on; // enable clock, either via init mode or by xcvr_on
reg spi_clk_f_on; // init clock mode, i.e. start clock but no tx/rx
reg [6:0] spi_clk_count; // counting cpu clock ticks
reg [6:0] spi_clk_div; // tick counter for spi clock phases
wire spi_clk_count_z = (spi_clk_count == 7'b0);
reg hphase_start; // start of a spi clock half-phase
assign tx_ready = !tx_fifo_full;
assign tx_empty = tx_fifo_empty;
assign rx_avail = !rx_fifo_empty;
assign rx_ovr = rx_overrun;
assign rx_data = rx_fifo_out;
assign card_busy = (sd_miso == 0);
assign card_changed = c_changed;
assign sd_sck = spi_clk;
assign sd_cs_n = ~c_cs;
assign card_detect = sd_cd;
fifo #(.ADDR_WIDTH(4)) tx_fifo(clk, reset,
tx_fifo_wr_en, tx_fifo_rd_en,
tx_data, tx_fifo_out,
tx_fifo_full,
tx_fifo_empty
);
fifo #(.ADDR_WIDTH(8)) rx_fifo(clk, reset,
rx_fifo_wr_en, rx_fifo_rd_en,
rx_shifter, rx_fifo_out,
rx_fifo_full,
rx_fifo_empty
);
// spi clock
always @(posedge clk)
begin
if(reset)
begin
spi_clk <= 1; // CLK is high when inactive
spi_clk_on <= 0;
spi_clk_count <= 0;
end
else if(spi_clk_on)
begin
// set spi_clk at start of every half-phase
if(hphase_start)
case(clk_phase)
CLKPHASE_0A: spi_clk <= 1'b0;
CLKPHASE_0B: spi_clk <= 1'b0;
CLKPHASE_1A: spi_clk <= 1'b1;
CLKPHASE_1B: spi_clk <= 1'b1;
endcase
if(spi_clk_count_z)
begin
spi_clk_count <= spi_clk_div;
clk_phase <= clk_phase + 2'b1;
end
else
spi_clk_count <= spi_clk_count - 7'd1;
end
// start the clock if needed
if( (spi_clk_on == 0) && (running || spi_clk_f_on))
begin
spi_clk_on <= 1;
spi_clk_count <= spi_clk_div;
clk_phase <= CLKPHASE_1A;
end
// turn off the clock if transceiver not running
// and the force-clock-on flag is not set
if( (spi_clk_on == 1) && (!running && !spi_clk_f_on))
begin
spi_clk_on <= 0;
spi_clk <= 1'b1;
end
end
// half-phase-start flag trails spi_clk_count_z by one tick
always @(posedge clk)
begin
if(reset)
hphase_start <= 0;
else
hphase_start <= spi_clk_on && spi_clk_count_z;
end
// handle the force clock enable flag
always @(posedge clk)
begin
if (reset)
spi_clk_f_on <= 0;
else
if (ctrl_write)
spi_clk_f_on <= spiclk_f_en;
end
// clock divider
always @(posedge clk)
begin
if (spiclk_div_wr) spi_clk_div <= tx_data[6:0];
end
// card_changed flag
always @(posedge clk)
begin
if(sd_cd)
c_changed <= 1;
else if(ctrl_write || reset)
c_changed <= 0;
end
// cs signal
always @(posedge clk)
begin
if(hphase_start && clk_phase == CLKPHASE_0A || !running)
c_cs <= running;
end
// transmitter
always @(posedge clk)
begin
if(reset)
begin
// ???? we start the bitcount at 1 because we start
// at the second clock phase where the bitcount
// is decremented and the next byte gets loaded
xcvr_bitcount <= 0;
tx_shifter <= 8'b1;
xcvr_on <= 0;
sd_mosi <= 1;
tx_fifo_rd_en <= 0;
end
else
begin
// handle a control write to disable the transceiver
if(ctrl_write && !txrx_en && xcvr_on)
xcvr_on <= 0;
// a byte might still be in transit, so
// we do not disable the transceiver
// immediately (see "handle running status" below)
else
// handle control write to enable the transceiver
if(ctrl_write && txrx_en && !running)
begin
xcvr_on <= 1;
xcvr_bitcount <= 0;
// next clock phase must be 1B when starting the transceiver,
// so that the first byte is loaded into the shifter then
tx_shifter <= 8'b11111111;
// in case the transceiver is enabled, but no data is in the fifo,
// initialize the shifter with $FF
end
else
// handle clock phases
if (running)
begin
if(hphase_start)
case(clk_phase)
// set mosi signal at start of clock pulse
CLKPHASE_0A: sd_mosi <= tx_shifter[7];
CLKPHASE_0B: ;
CLKPHASE_1A: begin // shift at rising clock
tx_shifter <= tx_shifter << 1;
xcvr_bitcount <= xcvr_bitcount - 1;
end
CLKPHASE_1B: begin // in the middle of the high clock pulse,
// fetch the next byte if there are no bits
// left in the shift register
if (xcvr_bitcount == 0)
begin
if(!tx_fifo_empty)
begin
tx_shifter <= tx_fifo_out;
tx_fifo_rd_en <= 1;
end
else
tx_shifter <= 8'b11111111;
xcvr_bitcount <= 8;
end
else
tx_fifo_rd_en <= 0;
end
endcase
else
tx_fifo_rd_en <= 0;
end
end
end
// handle data write
assign tx_fifo_wr_en = tx_write && !tx_fifo_full;
// Enable fifo read signal if fifo is not empty.
// The data at the fifo tail is always available at
// rx_data, the read signal just moves the tail pointer
// forward.
assign rx_fifo_rd_en = rx_read && !rx_fifo_empty;
// receiver
always @(posedge clk)
begin
if(reset)
begin
rx_bit_recvd <= 0;
rx_shifter <= 8'b11111111;
rx_fifo_wr_en <= 0;
rx_filter <= 0;
rx_overrun <= 0;
end
else
begin
// handle a control write
if(ctrl_write)
begin
rx_filter <= rx_filter_en;
rx_overrun <= 0;
if(txrx_en && !running)
rx_shifter <= 8'b0;
end
if (running && hphase_start)
case(clk_phase)
CLKPHASE_0A: ;
CLKPHASE_0B: ;
CLKPHASE_1A: ;
CLKPHASE_1B: begin // in the middle of the high clock pulse,
// sample MISO and put into shift register
// and shift at the same time
rx_shifter <= { rx_shifter[6:0],sd_miso};
rx_bit_recvd <= 1;
end
endcase
if (rx_bit_recvd && !sd_cs_n && clk_phase == CLKPHASE_1B)
begin
rx_bit_recvd <= 0;
// if a complete byte was received, bitcount will be
// 8 because the transmitter has already loaded the next byte
// at this half-phase
if (xcvr_bitcount == 8)
begin
// discard $FF bytes if filter is enabled
if(!rx_filter || rx_shifter != 8'b11111111)
begin
if(rx_fifo_full) // discard received byte if fifo is full
rx_overrun <= 1; // and set overrun flag
else
rx_fifo_wr_en <= 1; // otherwise, enable fifo write strobe,
// fifo will take data from rx_shifter
end
// turn off filter if a byte != $FF was received
if (rx_filter && rx_shifter != 8'b11111111)
rx_filter <= 0;
end
end
else
rx_fifo_wr_en <= 0;
end
end
// handle running status
// (especially keep transmitter running when there are still bits left to be
// transmitted)
always@(posedge clk)
begin
if (reset)
running <= 0;
else
begin
// if we want to turn the transceiver on, set running flag
if (!running && xcvr_on)
running <= 1;
// when running and a byte has been transmitted,
// check if we should turn the transceiver off
if (running && hphase_start && xcvr_bitcount==0)
if(clk_phase == CLKPHASE_1B)
if (!xcvr_on)
running <= 0;
end
end
endmodule