📄 simple_spi_top.v
字号:
//
// Motorola MC68HC11E based SPI interface
//
// Currently only MASTER mode is supported
//
// synopsys translate_off
`include "timescale.v"
// synopsys translate_on
module spi_top(
// 8bit APB bus slave interface
input wire pclk, // clock
input wire prst, // reset (asynchronous active low)
input wire psel, // select slave device and a data transfer required //
input wire penable, // penable
input wire [1:0] paddr, // address
input wire pwrite, // write enable
input wire [7:0] pwdata, // data input
output reg [7:0] prdata, // data output
output reg inta_o, // interrupt output
// SPI port
output reg sck_o, // serial clock output
output wire mosi_o, // MasterOut SlaveIN
input wire miso_i // MasterIn SlaveOut
);
reg [7:0] spcr; // Serial Peripheral Control Register ('HC11 naming)
wire [7:0] spsr; // Serial Peripheral Status register ('HC11 naming)
reg [7:0] sper; // Serial Peripheral Extension register
reg [7:0] treg, rreg; // Transmit/Receive register
// fifo signals
wire [7:0] rfdout;
reg wfre, rfwe;
wire rffull, rfempty;
wire [7:0] wfdout;
wire wffull, wfempty;
// misc signals
wire tirq; // transfer interrupt (selected number of transfers done)
wire wfov; // write fifo overrun (writing while fifo full)
reg [1:0] state; // statemachine state
reg [2:0] bcnt;
reg ack; // normal bus termination
// APB interface
wire reg_we = psel & pwrite; // APB write access
// pwdata
always @(posedge pclk or negedge prst)
if (~prst)
begin
spcr <= #1 8'h10; // set master bit
sper <= #1 8'h00;
end
else if ( reg_we )
begin
if (paddr == 2'b00)
spcr <= #1 pwdata | 8'h10; // always set master bit
if (paddr == 2'b11)
sper <= #1 pwdata;
end
// write fifo
wire wfwe = psel & paddr == 2'b10 & penable & pwrite;
wire rfre = psel & paddr == 2'b10 & penable & ~pwrite;
assign wfov = wfwe & wffull;
// prdata
always @( paddr or spcr or spsr or rfdout or sper )
case(paddr) // synopsys full_case parallel_case
2'b00: prdata <= #1 spcr;
2'b01: prdata <= #1 spsr;
2'b10: prdata <= #1 rfdout;
2'b11: prdata <= #1 sper;
endcase
// decode Serial Peripheral Control Register
wire spie = spcr[7]; // Interrupt enable bit
wire spe = spcr[6]; // System Enable bit
wire dwom = spcr[5]; // Port D Wired-OR Mode Bit
wire mstr = spcr[4]; // Master Mode Select Bit
wire cpol = spcr[3]; // Clock Polarity Bit
wire cpha = spcr[2]; // Clock Phase Bit
wire [1:0] spr = spcr[1:0]; // Clock Rate Select Bits
// decode Serial Peripheral Extension Register
wire [1:0] icnt = sper[7:6]; // interrupt on transfer count
wire [1:0] spre = sper[1:0]; // extended clock rate select
wire [3:0] espr = {spre, spr};
// generate status register
wire wr_spsr = reg_we & (paddr == 2'b01);
reg spif;
always @(posedge pclk)
if (~spe) spif <= #1 1'b0;
else spif <= #1 ( tirq | spif ) & ~(wr_spsr & pwdata[7]);
reg wcol;
always @(posedge pclk)
if (~spe) wcol <= #1 1'b0;
else wcol <= #1 (wfov | wcol) & ~(wr_spsr & pwdata[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;
// generate IRQ output (inta_o)
always @(posedge pclk)
inta_o <= #1 spif & spie;
// hookup read/write buffer fifo
fifo4 #(8)
rfifo(
.clk ( pclk ),
.rst ( prst ),
.clr ( ~spe ),
.din ( treg ),
.we ( rfwe ),
.dout ( rfdout ),
.re ( rfre ),
.full ( rffull ),
.empty ( rfempty )
),
wfifo(
.clk ( pclk ),
.rst ( prst ),
.clr ( ~spe ),
.din ( pwdata ),
.we ( wfwe ),
.dout ( wfdout ),
.re ( wfre ),
.full ( wffull ),
.empty ( wfempty )
);
// generate clk divider
reg [11:0] clkcnt;
always @(posedge pclk)
if( spe & (|clkcnt & |state) )
clkcnt <= #1 clkcnt - 11'h1;
else
case (espr) // synopsys full_case parallel_case
4'b0000: clkcnt <= #1 12'h0; // 2 -- original M68HC11 coding
4'b0001: clkcnt <= #1 12'h1; // 4 -- original M68HC11 coding
4'b0010: clkcnt <= #1 12'h7; // 16 -- original M68HC11 coding
4'b0011: clkcnt <= #1 12'hf; // 32 -- original M68HC11 coding
4'b0100: clkcnt <= #1 12'h3; // 8
4'b0101: clkcnt <= #1 12'h1f; // 64
4'b0110: clkcnt <= #1 12'h3f; // 128
4'b0111: clkcnt <= #1 12'h7f; // 256
4'b1000: clkcnt <= #1 12'hff; // 512
4'b1001: clkcnt <= #1 12'h1ff; // 1024
4'b1010: clkcnt <= #1 12'h3ff; // 2048
4'b1011: clkcnt <= #1 12'h7ff; // 4096
endcase
// generate clock enable signal
wire ena = ~| clkcnt;
// transfer statemachine
always @(posedge pclk)
if (~spe)
begin
state <= #1 2'b00; // idle
bcnt <= #1 3'h0;
treg <= #1 8'h00;
wfre <= #1 1'b0;
rfwe <= #1 1'b0;
sck_o <= #1 1'b0;
end
else
begin
wfre <= #1 1'b0;
rfwe <= #1 1'b0;
case (state) //synopsys full_case parallel_case
2'b00: // idle state
begin
bcnt <= #1 3'h7; // set transfer counter
treg <= #1 wfdout; // load transfer register
sck_o <= #1 cpol; // set sck
if (~wfempty) begin
wfre <= #1 1'b1;
state <= #1 2'b01;
if (cpha) sck_o <= #1 ~sck_o;
end
end
2'b01: // clock-phase2, next data
if (ena) begin
sck_o <= #1 ~sck_o;
state <= #1 2'b11;
end
2'b11: // clock phase1
if (ena) begin
treg <= #1 {treg[6:0], miso_i};
bcnt <= #1 bcnt -3'h1;
if (~|bcnt) begin
state <= #1 2'b00;
sck_o <= #1 cpol;
rfwe <= #1 1'b1;
end
else begin
state <= #1 2'b01;
sck_o <= #1 ~sck_o;
end
end
2'b10: state <= #1 2'b00;
endcase
end
assign mosi_o = treg[7];
// count number of transfers (for interrupt generation)
reg [1:0] tcnt; // transfer count
always @(posedge pclk)
if (~spe)
tcnt <= #1 icnt;
else if (rfwe) // rfwe gets asserted when all bits have been transfered
if (|tcnt) tcnt <= #1 tcnt - 2'h1;
else tcnt <= #1 icnt;
assign tirq = ~| tcnt & rfwe;
endmodule
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -