MIPS/resources/block/godson_sbridge_spi.v
2022-08-13 15:40:54 +08:00

897 lines
28 KiB
Verilog

/*------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Copyright (c) 2016, Loongson Technology Corporation Limited.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of Loongson Technology Corporation Limited nor the names of
its contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL LOONGSON TECHNOLOGY CORPORATION LIMITED BE LIABLE
TO ANY PARTY FOR DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
------------------------------------------------------------------------------*/
module spi_flash_ctrl(
input aclk,
input aresetn,
input [15:0] spi_addr,
input power_down_req,
output power_down_ack,
input fast_startup,
input [3:0] s_awlen,
input [3:0] s_awcache,
input [3:0] s_awid,
input [31:0] s_awaddr,
input [2:0] s_awsize,
input [2:0] s_awprot,
input [1:0] s_awburst,
input [1:0] s_awlock,
input s_awvalid,
output s_awready,
input [3:0] s_wid,
input [31:0] s_wdata,
input [3:0] s_wstrb,
input s_wlast,
input s_wvalid,
output s_wready,
output [3:0] s_bid,
output [1:0] s_bresp,
output s_bvalid,
input s_bready,
input [3:0] s_arlen,
input [3:0] s_arcache,
input [3:0] s_arid,
input [31:0] s_araddr,
input [2:0] s_arsize,
input [2:0] s_arprot,
input [1:0] s_arburst,
input [1:0] s_arlock,
input s_arvalid,
output s_arready,
output [3:0] s_rid,
output [31:0] s_rdata,
output [1:0] s_rresp,
output s_rlast,
output s_rvalid,
input s_rready,
output [3:0] csn_o,
output [3:0] csn_en,
output sck_o,
input sdo_i,
output sdo_o,
output sdo_en,
input sdi_i,
output sdi_o,
output sdi_en,
output inta_o
);
wire areset = ~aresetn;
wire param_memory_en;
wire param_burst_en;
wire param_fast_read;
wire param_dual_io;
wire [1:0] param_tCSH;
wire param_tFAST;
reg [9:0] rd_state;
reg [9:0] rd_state_nxt;
parameter S_IDLE = 10'b0000000001;
parameter S_IOREAD = 10'b0000000010;
parameter S_CSTURN = 10'b0000000100;
parameter S_ADDR = 10'b0000001000;
parameter S_DATA = 10'b0000010000;
parameter S_WAITBUS= 10'b0000100000;
parameter S_PDENTER= 10'b0001000000;
parameter S_PDEXIT = 10'b0010000000;
parameter S_STARTUP= 10'b0100000000;
parameter S_PWRDOWN= 10'b1000000000;
wire s_idle = rd_state[0];
wire s_ioread = rd_state[1];
wire s_csturn = rd_state[2];
wire s_addr = rd_state[3];
wire s_data = rd_state[4];
wire s_waitbus= rd_state[5];
wire s_pdenter= rd_state[6];
wire s_pdexit = rd_state[7];
wire s_startup= rd_state[8];
wire s_pwrdown= rd_state[9];
wire ns_idle = rd_state_nxt[0];
wire ns_ioread = rd_state_nxt[1];
wire ns_csturn = rd_state_nxt[2];
wire ns_addr = rd_state_nxt[3];
wire ns_data = rd_state_nxt[4];
wire ns_waitbus= rd_state_nxt[5];
wire ns_pdenter= rd_state_nxt[6];
wire ns_pdexit = rd_state_nxt[7];
wire ns_startup= rd_state_nxt[8];
wire ns_pwrdown= rd_state_nxt[9];
reg pdreq_r;
reg [15:0] cs_timer;
reg cs;
reg [23:0] nxt_addr;
wire write_valid;
wire reg_acc = s_ioread | write_valid;
wire reg_ack;
wire [7:0] reg_dat_i, reg_dat_o;
wire [7:0] param_o;
reg [31:0] shift_reg;
reg [ 1:0] sample;
wire [31:0] shift_reg_nxt;
wire sr_shift_inst;
wire sr_shift_one;
wire sr_shift_two;
reg sr_shift_inst_r;
reg sr_shift_two_r;
wire sample_en;
wire shift_en;
wire dual_out;
wire dual_in;
wire [1:0] serial_out;
wire cyc_end;
reg [2:0] bit_cnt;
wire spi_pause;
wire spibus_busy;
reg [5:0] adbit_cnt;
reg spi_run;
reg sck;
reg buf_busy;
reg [31:0] buf_addr;
reg [ 3:0] buf_len;
reg [ 2:0] buf_size;
reg [ 3:0] buf_id;
reg buf_write;
reg buf_wrap;
assign s_arready = s_idle & ~pdreq_r & ~buf_busy & ~s_awvalid;
assign s_awready = s_idle & ~pdreq_r & ~buf_busy;
reg buf_busy_d;
wire new_axireq = ~buf_busy_d & buf_busy;
wire io_hit =(buf_addr[31:4] == {spi_addr, 12'b0}) &
(buf_len == 4'b0);
wire [63:0] buf_addr_t = (buf_addr[31:20]==12'h1fc)?
{12'h0, buf_addr[19:0]}:
{ 8'h0, buf_addr[23:0]};
wire burst_cont = param_burst_en & cs &
(buf_addr_t[23:0] == nxt_addr[23:0]);
wire burst_switch = param_burst_en & cs &
(buf_addr_t[23:0] != nxt_addr[23:0]);
reg [7:0] tot_bytes;
wire byte_ready;
always @(posedge aclk) begin
if (areset) begin
buf_busy <= 1'b0;
buf_write <= 1'b0;
tot_bytes <= 8'b0;
end else begin
if ((s_arvalid|s_awvalid)&~buf_busy&s_idle&~pdreq_r) begin
buf_busy <= 1'b1;
buf_addr <= s_awvalid ? s_awaddr : s_araddr;
buf_size <= s_awvalid ? s_awsize : s_arsize;
buf_len <= s_awvalid ? s_awlen : s_arlen;
buf_id <= s_awvalid ? s_awid : s_arid;
buf_write<= s_awvalid;
buf_wrap <= s_arvalid & (s_arburst==2'b10) &
(|s_araddr[4:2]) & (|s_arlen);
tot_bytes<= {8{s_arvalid&~s_awvalid}}&
(({4'b0,s_arlen} << s_arsize)|
((8'b1<<s_arsize)-8'b1));
end else begin
if (s_bvalid & s_bready | s_rvalid & s_rready & s_rlast)
buf_busy <= 1'b0;
if (s_rvalid & s_rready)
buf_len <= buf_len - 4'b1;
if (s_wvalid & s_wready & s_wlast)
buf_write <= 1'b0;
if (byte_ready & ~s_rvalid) begin
tot_bytes <= tot_bytes - 8'b1;
end
end
end
buf_busy_d <= buf_busy;
end
reg second_write;
always @(posedge aclk) begin
if (areset) second_write <= 1'b0;
else second_write <= (s_wvalid & s_wready & io_hit & (buf_size==3'b1) & (buf_addr[2:0]==3'b10));
end
assign s_wready = buf_busy & buf_write & s_idle;
assign write_valid = s_wvalid & s_wready & io_hit &
((buf_size==3'b0) | (buf_size==3'b1 && buf_addr[2:0]==3'b10)) |
second_write;
reg bvalid;
always @(posedge aclk) begin
if (areset ) bvalid <= 1'b0;
else if (s_bvalid & s_bready ) bvalid <= 1'b0;
else if (s_wvalid & s_wready & s_wlast) bvalid <= 1'b1;
end
assign s_bvalid = bvalid;
assign s_bid = buf_id;
assign s_bresp = 2'b00;
reg rvalid;
reg [7:0] rdata[3:0];
always @(posedge aclk) begin
if (areset)
rvalid <= 1'b0;
else if (s_rvalid & s_rready) begin
rvalid <= 1'b0;
end else if (new_axireq & ~buf_write & io_hit & s_idle) begin
rvalid <= 1'b1;
rdata[buf_addr[1:0]] <= reg_dat_o;
end else if (s_data & byte_ready & ~rvalid) begin
rvalid <= (&nxt_addr[1:0]) | (~|tot_bytes);
rdata[nxt_addr[1:0]] <= shift_reg_nxt[7:0];
end
end
assign s_rvalid = rvalid;
assign s_rdata = {rdata[ 3], rdata[ 2], rdata[ 1], rdata[ 0]};
assign s_rlast = ~|buf_len;
assign s_rid = buf_id;
assign s_rresp = 2'b00;
wire [1:0] sample_in = {2{s_data}}&(param_tFAST ? {sdi_i, sdo_i} :
sample[1:0] );
assign shift_reg_nxt = sr_shift_inst_r?{shift_reg[30:0], 1'b0 }:
sr_shift_two_r ?{shift_reg[29:0], sample_in[1:0]}:
{shift_reg[30:0], sample_in[1] };
always @(posedge aclk) begin
if (s_pwrdown & ~ns_pwrdown) begin
shift_reg[31:24] <= 8'hab;
end else if (~s_pdenter & ns_pdenter) begin
shift_reg[31:24] <= 8'hb9;
end else if (~s_addr & ns_addr) begin
shift_reg[31:24] <= param_dual_io ? 8'hbb:
param_fast_read ? 8'h0b:
8'h03;
shift_reg[23: 0] <= nxt_addr[23:0];
end else if (shift_en) begin
shift_reg[31: 0] <= shift_reg_nxt;
end
if (sample_en) sample[1:0] <= {sdi_i, sdo_i};
end
assign serial_out = param_dual_io & dual_out ? shift_reg[31:30] :
{1'b0, shift_reg[31]};
wire [3:0] espr;
reg [11:0] clkcnt;
wire clkena = ~|clkcnt & ~spi_pause;
reg [3:0] cswcnt;
always @(posedge aclk)
if (areset)
clkcnt <= 12'h0;
else if (~spi_pause) begin
if ((|clkcnt) & (spi_run|s_csturn))
clkcnt <= clkcnt - 11'h1;
else
case (espr) // synopsys full_case parallel_case
4'b0000: clkcnt <= 12'h0;
4'b0001: clkcnt <= 12'h1;
4'b0010: clkcnt <= 12'h7;
4'b0011: clkcnt <= 12'hf;
4'b0100: clkcnt <= 12'h3;
4'b0101: clkcnt <= 12'h1f;
4'b0110: clkcnt <= 12'h3f;
4'b0111: clkcnt <= 12'h7f;
4'b1000: clkcnt <= 12'hff;
4'b1001: clkcnt <= 12'h1ff;
4'b1010: clkcnt <= 12'h3ff;
4'b1011: clkcnt <= 12'h7ff;
default: clkcnt <= 12'h7ff;
endcase
end
always @(posedge aclk)
if (areset|~s_csturn) cswcnt <= 4'b0;
else if (clkena) cswcnt <= cswcnt + 4'b1;
wire [3:0] cswcnt_w = cswcnt | (4'b1110 << param_tCSH);
always @(posedge aclk) begin
if (areset ) spi_run <= 1'b0;
else if (ns_addr|ns_data) spi_run <= 1'b1;
else if (ns_idle|ns_csturn)spi_run <= 1'b0;
else if (ns_pdexit |ns_pdenter)spi_run <= 1'b1;
else if (ns_startup|ns_pwrdown)spi_run <= 1'b0;
if (s_idle |s_csturn) adbit_cnt <= 6'b0;
else if (s_addr & cyc_end) adbit_cnt <= adbit_cnt + 6'b1;
if (areset ) sck <= 1'b0;
else if (spi_run & clkena) sck <= ~sck;
if (areset | s_idle ) bit_cnt <= 3'h0;
else if (s_data & cyc_end) bit_cnt <= bit_cnt + 3'b1;
else if (s_pdenter&cyc_end)bit_cnt <= bit_cnt + 3'b1;
else if (s_pdexit &cyc_end)bit_cnt <= bit_cnt + 3'b1;
end
assign byte_ready = s_data & cyc_end & (&({param_dual_io,2'b00}|bit_cnt[2:0]));
always @(posedge aclk) begin
if (areset | ~param_memory_en) begin
nxt_addr <= 24'b0;
cs_timer <= 16'b0;
cs <= 1'b0;
end else begin
nxt_addr <= new_axireq & s_idle ? buf_addr_t :
byte_ready & ~spi_pause? (buf_wrap&(&nxt_addr[4:0]) & ~(tot_bytes == 8'b0)?
nxt_addr - 24'h1f :
nxt_addr + 24'b1) :
nxt_addr;
cs_timer <= buf_busy|(~cs&~s_startup)|s_pdexit ? 16'b0 :
~&cs_timer ? cs_timer+16'b1 :
cs_timer ;
cs <= ns_addr ? 1'b1 :
ns_csturn | (~buf_busy & (&cs_timer)) ? 1'b0 :
~param_burst_en & ns_idle ? 1'b0 :
write_valid & (buf_addr[3:0]==4'h2) ? 1'b0 :
ns_pdenter | ns_pdexit ? 1'b1 :
ns_pwrdown | ns_startup ? 1'b0 :
cs;
end
end
assign cyc_end = spi_run & sck & clkena;
assign shift_en = spi_run & sck & clkena;
assign sample_en = spi_run &~sck & clkena & s_data;
assign spi_pause = rvalid;
assign sr_shift_inst = s_addr & (adbit_cnt < 6'd8);
assign sr_shift_two =(s_addr & (adbit_cnt >=6'd8) | s_data) & param_dual_io;
always @(posedge aclk) begin
sr_shift_inst_r <= areset ? 1'b0 :
cyc_end|(~s_addr&ns_addr) ? ns_addr & (adbit_cnt < 6'd7) :
sr_shift_inst_r;
sr_shift_two_r <= areset ? 1'b0 :
cyc_end|(~s_data&ns_data) ? (s_addr & (adbit_cnt >=6'd7) | ns_data) & param_dual_io :
sr_shift_two_r;
end
assign sr_shift_one = 1'bz;
wire addr_done;
assign addr_done = param_dual_io ? adbit_cnt == 6'd23 :
param_fast_read ? adbit_cnt == 6'd39 :
adbit_cnt == 6'd31 ;
assign dual_out = param_dual_io &
(adbit_cnt >= 6'd8 && adbit_cnt < 6'd22);
reg dual_in_r;
assign dual_in = param_dual_io &
(adbit_cnt >= 6'd22 | s_data | dual_in_r);
always @(posedge aclk) begin
dual_in_r <= areset ? 1'b0 :
s_csturn&cswcnt[0]? 1'b0 :
~cs ? 1'b0 :
dual_in ? 1'b1 : dual_in_r;
end
always @(posedge aclk) begin
pdreq_r <= power_down_req;
end
wire go_power_down = pdreq_r & ~buf_busy;
assign power_down_ack = s_pwrdown | s_pdexit | s_startup;
always @(posedge aclk) begin
rd_state <= areset ? S_PWRDOWN : rd_state_nxt;
end
always @(*) begin
rd_state_nxt = rd_state;
case (rd_state) // synopsys parallel_case
S_IDLE :if (new_axireq & ~buf_write) begin
rd_state_nxt = io_hit ? S_IOREAD:
spibus_busy ? S_WAITBUS:
burst_cont ? S_DATA :
S_CSTURN;
end else if (go_power_down) begin
rd_state_nxt = cs ? S_CSTURN :
S_PDENTER;
end
S_IOREAD: rd_state_nxt = S_IDLE;
S_CSTURN: rd_state_nxt = clkena & (&cswcnt_w)? (go_power_down? S_PDENTER:S_ADDR):
S_CSTURN;
S_ADDR : rd_state_nxt = clkena & sck &
addr_done ? S_DATA : S_ADDR;
S_DATA : rd_state_nxt = byte_ready & ~spi_pause & ~|tot_bytes ? S_IDLE:
byte_ready & ~spi_pause & buf_wrap
& (&nxt_addr[4:0])? S_CSTURN:
byte_ready & ~spi_pause & ~param_burst_en ? S_CSTURN:
S_DATA;
S_WAITBUS:rd_state_nxt = spibus_busy ? S_WAITBUS : S_ADDR;
S_PWRDOWN:rd_state_nxt = go_power_down ? S_PWRDOWN : S_PDEXIT;
S_PDEXIT :rd_state_nxt = cyc_end & (&bit_cnt[2:0]) ? S_STARTUP : S_PDEXIT;
S_PDENTER:rd_state_nxt = cyc_end & (&bit_cnt[2:0]) ? S_PWRDOWN : S_PDENTER;
S_STARTUP:rd_state_nxt = &(cs_timer[10:0]|{{5{fast_startup}}, 6'b0}) ? S_IDLE :
S_STARTUP;
endcase
end
wire ss_sck, ss_mosi, ss_miso;
wire [7:0] param, softcs, param2;
wire sspi_write = buf_write | second_write;
simple_spi_top simple_spi(
.clk_i (aclk ),
.rst_i (aresetn ),
.cyc_i (buf_busy ),
.stb_i (reg_acc ),
.adr_i (buf_addr[3:0] ),
.we_i (sspi_write ),
.dat_i (reg_dat_i ),
.dat_o (reg_dat_o ),
.ack_o (reg_ack ),
.inta_o (inta_o ),
.sck_o (ss_sck ),
.mosi_o (ss_mosi ),
.miso_i (ss_miso ),
.param (param ),
.param2 (param2 ),
.softcs (softcs ),
.busy (spibus_busy )
);
assign ss_miso = sdi_i;
assign reg_dat_i = second_write ? s_wdata[ 31: 24] :
buf_addr[1:0]==2'h0 ? s_wdata[ 7: 0] :
buf_addr[1:0]==2'h1 ? s_wdata[ 15: 8] :
buf_addr[1:0]==2'h2 ? s_wdata[ 23: 16] :
s_wdata[ 31: 24] ;
assign param_memory_en = param[0];
assign param_burst_en = param[1];
assign param_fast_read = param[2];
assign param_dual_io = param[3];
assign espr = param[7:4];
assign param_tCSH = param2[1:0];
assign param_tFAST = param2[2];
assign param_scs = param2[3];
assign csn_en[0] = param_memory_en? 1'b0: ~softcs[0];
assign csn_o [0] = param_memory_en? ~cs : softcs[4];
assign csn_en[3:1] =~softcs[3:1];
assign csn_o [3:1] = softcs[7:5]|{3{cs|(~spibus_busy & param_scs)}};
assign sdi_en = ~spibus_busy&param_memory_en? ~dual_out : 1'b1;
assign sdi_o = ~spibus_busy&param_memory_en? serial_out[1] : 1'b0;
assign sdo_en = ~spibus_busy&param_memory_en? dual_in : 1'b0;
assign sdo_o = ~spibus_busy&param_memory_en? serial_out[0] | s_data
: ss_mosi;
assign sck_o = ~spibus_busy&param_memory_en? sck : ss_sck;
endmodule
module simple_spi_top(
input wire clk_i,
input wire rst_i,
input wire cyc_i,
input wire stb_i,
input wire [3:0] adr_i,
input wire we_i,
input wire [7:0] dat_i,
output reg [7:0] dat_o,
output reg ack_o,
output reg inta_o,
output reg sck_o,
output wire mosi_o,
input wire miso_i,
output reg [7:0] param,
output reg [7:0] param2,
output reg [7:0] softcs,
output reg busy
);
reg [7:0] spcr;
wire [7:0] spsr;
reg [7:0] sper;
reg [7:0] treg, rreg;
wire [7:0] rfdout;
reg wfre, rfwe;
wire rfre, rffull, rfempty;
wire [7:0] wfdout;
wire wfwe, wffull, wfempty;
wire tirq;
wire wfov;
reg [1:0] state;
reg [2:0] bcnt;
wire wb_acc = cyc_i & stb_i;
wire wb_wr = wb_acc & we_i;
always @(posedge clk_i)
if (~rst_i)
begin
spcr <= 8'h12;
sper <= 8'h00;
`ifdef FAST_SIMU
param<= 8'h1;
param2<=8'h07;
`else
param<= 8'h1;
param2<=8'h03;
`endif
softcs<=8'hf0;
end
else if (wb_wr)
begin
if (adr_i == 4'b00)
spcr <= dat_i | 8'h10;
if (adr_i == 4'b11)
sper <= dat_i;
if (adr_i == 4'b0100)
param <= dat_i;
if (adr_i == 4'b0101)
softcs<= dat_i;
if (adr_i == 4'b0110)
param2 <= dat_i;
end
assign wfwe = wb_acc & (adr_i == 4'b10) & ack_o & we_i;
assign wfov = wfwe & wffull;
always @(*)
case(adr_i) // synopsys full_case parallel_case
4'b0000: dat_o = spcr;
4'b0001: dat_o = spsr;
4'b0010: dat_o = rfdout;
4'b0011: dat_o = sper;
4'b0100: dat_o = param;
4'b0101: dat_o = softcs;
4'b0110: dat_o = param2;
default dat_o = 8'h0;
endcase
assign rfre = wb_acc & (adr_i == 2'b10) & ack_o & ~we_i;
always @(posedge clk_i)
ack_o <= 1'b1;
wire spie = spcr[7];
wire spe = spcr[6];
wire dwom = spcr[5];
wire mstr = spcr[4];
wire cpol = spcr[3];
wire cpha = spcr[2];
wire [1:0] spr = spcr[1:0];
wire [1:0] icnt = sper[7:6];
wire [1:0] spre = sper[1:0];
wire smh_spi= sper[2];
wire [3:0] espr = {spre, spr};
wire wr_spsr = wb_wr & (adr_i == 2'b01);
reg spif;
always @(posedge clk_i)
if (~spe)
spif <= 1'b0;
else
spif <= (tirq | spif) & ~(wr_spsr & dat_i[7]);
reg wcol;
always @(posedge clk_i)
if (~spe)
wcol <= 1'b0;
else
wcol <= (wfov | wcol) & ~(wr_spsr & dat_i[6]);
assign spsr[7] = spif;
assign spsr[6] = wcol;
assign spsr[5:4] = 2'b00;
assign spsr[3] = wffull;
assign spsr[2] = wfempty;
assign spsr[1] = rffull;
assign spsr[0] = rfempty;
always @(posedge clk_i)
inta_o <= spif & spie;
spi_fifo4 #(8)
rfifo(
.clk ( clk_i ),
.rst ( rst_i ),
.clr ( ~spe ),
.din ( treg ),
.we ( rfwe ),
.dout ( rfdout ),
.re ( rfre ),
.full ( rffull ),
.empty ( rfempty )
),
wfifo(
.clk ( clk_i ),
.rst ( rst_i ),
.clr ( ~spe ),
.din ( dat_i ),
.we ( wfwe ),
.dout ( wfdout ),
.re ( wfre ),
.full ( wffull ),
.empty ( wfempty )
);
reg [11:0] clkcnt;
always @(posedge clk_i)
if(spe & (|clkcnt & |state))
clkcnt <= clkcnt - 11'h1;
else
case (espr) // synopsys full_case parallel_case
4'b0000: clkcnt <= 12'h0;
4'b0001: clkcnt <= 12'h1;
4'b0010: clkcnt <= 12'h7;
4'b0011: clkcnt <= 12'hf;
4'b0100: clkcnt <= 12'h3;
4'b0101: clkcnt <= 12'h1f;
4'b0110: clkcnt <= 12'h3f;
4'b0111: clkcnt <= 12'h7f;
4'b1000: clkcnt <= 12'hff;
4'b1001: clkcnt <= 12'h1ff;
4'b1010: clkcnt <= 12'h3ff;
4'b1011: clkcnt <= 12'h7ff;
default:;
endcase
wire ena = ~|clkcnt;
reg sample;
always @(posedge clk_i)
if (~spe)
begin
state <= 2'b00;
bcnt <= 3'h0;
treg <= 8'h00;
wfre <= 1'b0;
rfwe <= 1'b0;
sck_o <= 1'b0;
end
else if (smh_spi)
begin
wfre <= 1'b0;
rfwe <= 1'b0;
case (state) //synopsys full_case parallel_case
2'b00:
begin
bcnt <= 3'h7;
treg <= wfdout;
sck_o <= cpol;
if (~wfempty) begin
wfre <= 1'b1;
state <= 2'b01;
end
end
2'b01:
if (ena) begin
sck_o <= ~sck_o;
state <= 2'b10;
if (cpha==0) sample <= miso_i;
end
2'b10:
if (ena) begin
sck_o <= ~sck_o;
state <= 2'b11;
if (cpha==0) begin
treg <= {treg[6:0], sample};
end else begin
sample <= miso_i;
end
end
2'b11:
if (ena) begin
bcnt <= bcnt -3'h1;
if (cpha==0) begin
sample <= miso_i;
end else begin
treg <= {treg[6:0], sample};
end
if (~|bcnt) begin
state <= 2'b00;
sck_o <= cpol;
rfwe <= 1'b1;
end else begin
state <= 2'b10;
sck_o <= ~sck_o;
end
end
endcase
end
else
begin
wfre <= 1'b0;
rfwe <= 1'b0;
case (state) //synopsys full_case parallel_case
2'b00:
begin
bcnt <= 3'h7;
treg <= wfdout;
sck_o <= cpol;
if (~wfempty) begin
wfre <= 1'b1;
state <= 2'b01;
if (cpha) sck_o <= ~sck_o;
end
end
2'b01:
if (ena) begin
sck_o <= ~sck_o;
state <= 2'b11;
end
2'b11:
if (ena) begin
treg <= {treg[6:0], miso_i};
bcnt <= bcnt -3'h1;
if (~|bcnt) begin
state <= 2'b00;
sck_o <= cpol;
rfwe <= 1'b1;
end
else begin
state <= 2'b01;
sck_o <= ~sck_o;
end
end
2'b10: state <= 2'b00;
default: state <=2'b00;
endcase
end
assign mosi_o = treg[7];
reg [1:0] tcnt;
always @(posedge clk_i)
if (~spe)
tcnt <= icnt;
else if (rfwe) begin
if (|tcnt)
tcnt <= tcnt - 2'h1;
else
tcnt <= icnt;
end
assign tirq = ~|tcnt & rfwe;
always @(posedge clk_i)
busy <= ~wfempty | (|state);
endmodule
module spi_fifo4(clk, rst, clr, din, we, dout, re, full, empty);
parameter dw = 8;
input clk, rst;
input clr;
input [dw:1] din;
input we;
output [dw:1] dout;
input re;
output full, empty;
reg [dw:1] mem[0:3];
reg [1:0] wp;
reg [1:0] rp;
wire [1:0] wp_p1;
wire [1:0] wp_p2;
wire [1:0] rp_p1;
wire full, empty;
reg gb;
always @(posedge clk)
if(!rst) wp <= 2'h0;
else
if(clr) wp <= 2'h0;
else
if(we) wp <= wp_p1;
assign wp_p1 = wp + 2'h1;
assign wp_p2 = wp + 2'h2;
always @(posedge clk)
if(!rst) rp <= 2'h0;
else
if(clr) rp <= 2'h0;
else
if(re) rp <= rp_p1;
assign rp_p1 = rp + 2'h1;
assign dout = mem[ rp ];
always @(posedge clk)
if(we) mem[ wp ] <= din;
assign empty = (wp == rp) & !gb;
assign full = (wp == rp) & gb;
always @(posedge clk)
if(!rst) gb <= 1'b0;
else
if(clr) gb <= 1'b0;
else
if((wp_p1 == rp) & we) gb <= 1'b1;
else
if(re) gb <= 1'b0;
endmodule