initial commit
This commit is contained in:
commit
60db522e87
107 changed files with 36924 additions and 0 deletions
372
rtl/src/sdspi.v
Normal file
372
rtl/src/sdspi.v
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue