📄 vspi.v
字号:
wire shift_clk_negedge; // negative edge of SCK
wire shift_negative_edge_nxt;
reg shift_negative_edge;
wire shift_datain;
wire shift_dataout;
reg slvsel_r1;
reg slvsel_r2;
reg slvsel_r3; // synchronizers
wire spi_go; // begin a transfer
// slave select register
reg [7:0] ssel;
// status register
wire [7:0] status;
// TX has completed, TX_RUN will go low
wire tx_end;
reg tx_run; // tx is running
wire tx_start;
// -------------------------------------------------------------
reg tx_start_r1;
// ---------ARCHITECTURE rtl-----------
// required for synopsys
// # bits in a byte
assign mosio = mosie_lcl == 1'b 1 & open_drain == 1'b 0 ? shift_dataout :
1'b 0;
// drive low when open drain enabled.
assign mosie = open_drain == 1'b 0 ? mosie_lcl :
mosie_lcl == 1'b 1 & shift_dataout == 1'b 0 ? 1'b 1 :
1'b 0;
assign misoo = misoe_lcl == 1'b 1 & open_drain == 1'b 0 ? shift_dataout :
1'b 0;
// drive low when open drain enabled.
assign misoe = open_drain == 1'b 0 ? misoe_lcl :
misoe_lcl == 1'b 1 & shift_dataout == 1'b 0 ? 1'b 1 :
1'b 0;
assign misoe_lcl = master_mode == 1'b 0 & slvsel == 1'b 1 ? 1'b 1 :
1'b 0;
// spi_go initiates a transfer - A write to the DOUT reg in master
// mode ignore the CPU write if we're already running.
assign mosie_lcl = master_mode == 1'b 1 ? 1'b 1 :
1'b 0;
assign spi_go = chip_sel == 1'b 0 & write == 1'b 0 &
addr == 2'b 00 & tx_run == 1'b 0 &
slvsel_r3 == 1'b 0 ? 1'b 1 :
1'b 0;
// -----------Shift register----------------
always @(posedge clk)
begin : sr_proc
if (rst == 1'b 0)
begin
shift_reg <= 8'b 00000000; // sync reset
end
else
begin
if (spi_go == 1'b 1)
// don't reload while running
begin
shift_reg <= datain; // load with data from CPU
end
else if (shift_clk == 1'b 1 )
begin
shift_reg <= {shift_reg[6:0], shift_datain};
end
end
end
// --------Hold time register--------------
always @(posedge clk)
// negative edge pipeline DFF
begin : neg_proc
if (rst == 1'b 0)
begin
shift_negative_edge <= 1'b 0; // sync reset
end
else if (shift_clk_negedge == 1'b 1 )
begin
shift_negative_edge <= shift_negative_edge_nxt;
end
else if (spi_go == 1'b 1 )
begin
shift_negative_edge <= datain[7]; // preload for phase=0 mode
end
end
assign shift_negative_edge_nxt = phase == 1'b 1 ? shift_reg[7] :
master_mode == 1'b 1 ? misoi :
mosii;
// add in the negative edge dff on phase=1
assign shift_dataout = phase == 1'b 1 ? shift_negative_edge :
shift_reg[7];
// insert the neg DFF in phase=0
assign shift_datain = phase == 1'b 0 ? shift_negative_edge :
master_mode == 1'b 1 ? misoi :
mosii;
// -------------TX run------------------
// this bit is active while a transmit is running
always @(posedge clk)
begin : tr_proc
if (rst == 1'b 0)
begin
tx_run <= 1'b 0; // sync reset
end
else
begin
if (tx_start == 1'b 1)
begin
tx_run <= 1'b 1;
end
else if (tx_end == 1'b 1 )
begin
tx_run <= 1'b 0;
end
end
end
// -----------Bit counter for master mode----------------
always @(posedge clk)
begin : bc_proc
if (rst == 1'b 0)
// sync reset
begin
bit_ctr <= 3'b 000;
end
else
begin
if (tx_start == 1'b 1)
begin
bit_ctr <= ssel[7:5];
end
else if (shift_clk == 1'b 1 )
begin
bit_ctr <= bit_ctr - 1'b 1;
end
end
end
// bit counter
assign tx_end = master_mode == 1'b 1 & bit_ctr == 3'b 001 &
shift_clk == 1'b 1 & tx_run == 1'b 1 ? 1'b 1 :
1'b 0;
assign tx_start = master_mode == 1'b 1 & spi_go == 1'b 1 ? 1'b 1 :
1'b 0;
// -------Control Register----------------------
always @(posedge clk)
begin : gjr_proc
if (rst == 1'b 0)
// sync reset
begin
ctl_reg <= 8'b 00000000;
end
else
begin
if (chip_sel == 1'b 0 & write == 1'b 0 &
addr == 2'b 01)
// load
begin
ctl_reg <= datain;
end
end
end
// map the control register to more meaningfull names
assign master_mode = ctl_reg[1];
assign open_drain = ctl_reg[2];
assign polck = ctl_reg[3];
assign phase = ctl_reg[4];
assign sel_clk = ctl_reg[6:5];
// -------Slave Select Register-------------------------
always @(posedge clk)
begin : s_proc
if (rst == 1'b 0)
// sync reset
begin
ssel <= 8'b 00000000;
end
else
begin
if (chip_sel == 1'b 0 & write == 1'b 0 &
addr == 2'b 11)
// load
begin
ssel <= datain;
end
end
end
assign slvselo = ssel[4:0]; // drive the port
assign slvsele = master_mode;
// -------Collision flag bit---------------------------
always @(posedge clk)
begin : cf_proc
if (rst == 1'b 0)
begin
col_flag <= 1'b 0;
end
else
begin
if (master_mode == 1'b 1 & slvsel_r3 == 1'b 1)
begin
col_flag <= 1'b 1;
end
else if (chip_sel == 1'b 0 & write == 1'b 0 &
addr == 2'b 10 & datain[5] == 1'b 1 )
begin
col_flag <= 1'b 0;
end
end
end
// -------OFLOw flag bit------------------------------
always @(posedge clk)
begin : o_proc
if (rst == 1'b 0)
begin
oflow <= 1'b 0;
end
else
begin
if (chip_sel == 1'b 0 & write == 1'b 0 &
addr == 2'b 00 & (tx_run == 1'b 1 |
slvsel_r3 == 1'b 1))
// write to DOUT
// and we're busy
begin
oflow <= 1'b 1;
end
else if (chip_sel == 1'b 0 & write == 1'b 0 &
addr == 2'b 10 & datain[6] == 1'b 1 )
begin
oflow <= 1'b 0;
end
end
end
// -------IRQ flag bit------------------------------
always @(posedge clk)
begin : elr_proc
if (rst == 1'b 0)
begin
irq_flag <= 1'b 0;
end
else
begin
if (tx_end == 1'b 1 | slvsel_r2 == 1'b 0 &
slvsel_r3 == 1'b 1)
begin
irq_flag <= 1'b 1;
end
/*
else if (chip_sel == 1'b 1 & write == 1'b 1 &
addr == 2'b 10 & datain[7] == 1'b 1 )
*/
else
begin
irq_flag <= 1'b 0;
end
end
end
assign irq = irq_flag & ctl_reg[7]; // gate with the IRQENB bit.
// --------------various pipeline flops---------
always @(posedge clk)
begin : flops_proc
slvsel_r3 <= slvsel_r2;
slvsel_r2 <= slvsel_r1; // synchronizers
slvsel_r1 <= slvsel;
sck_r3 <= sck_r2;
sck_r2 <= sck_r1; // synchronizers
// select the desired polarity of the slave clk
sck_r1 <= ~scki ^ polck;
tx_start_r1 <= tx_start;
end
// --------------clock divider for clk generation-------
// create a 2x clock which creates 2 pulses.
// One for each edge of SCK.
always @(posedge clk)
begin : dvd_proc
if (~(tx_run == 1'b 1 & master_mode == 1'b 1) |
tx_end == 1'b 1)
// divider only runs when sending data
begin
dvd_ctr <= 5'b 00000;
dvd2 <= 1'b 0;
end
else
begin
if (dvd_ctr == 5'b 00000)
begin
if (sel_clk == 2'b 00)
begin
dvd_ctr <= 5'b 00011;
end
else if (sel_clk == 2'b 01 )
begin
dvd_ctr <= 5'b 00111;
end
else if (sel_clk == 2'b 10 )
begin
dvd_ctr <= 5'b 01111;
end
else
begin
dvd_ctr <= 5'b 11111;
end
if (tx_start_r1 == 1'b 0)
begin
dvd2 <= ~dvd2;
end
end
else
begin
dvd_ctr <= dvd_ctr - 1'b 1;
end
end
end
// dvd
assign dvd_zero = dvd_ctr == 5'b 00000 ? 1'b 1 :
1'b 0;
// TX_START_R1 prevents data from shifting on the first
// clock in POLCK=1 mode which we don't want.We only get
// 7 clocks otherwise.
assign shift_clk = master_mode == 1'b 1 ? dvd_zero & dvd2 & tx_run &
~tx_start_r1 :
sck_r2 & ~sck_r3;
// dataout multiplexor for register readback
// assemble the bits that make up the status register
assign shift_clk_negedge = master_mode == 1'b 1 ? dvd_zero & ~dvd2 & tx_run :
~sck_r2 & sck_r3;
assign status = {irq_flag, oflow, col_flag, 3'b 000,
tx_run, slvsel_r3};
assign scke = master_mode;
assign scko = dvd2 ^ polck;
assign dataout = addr == 2'b 00 ? shift_reg :
addr == 2'b 01 ? ctl_reg :
addr == 2'b 10 ? status :
addr == 2'b 11 ? ssel :
8'b xxxxxxxx;
endmodule // module vspi
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -