1.SPI
①SPI 是英语Serial Peripheral interface的缩写,也就是串行外围设备接口
②它是一种高速的,全双工,同步的通信总线,只占用四根线,主要应用在 EEPROM,FLASH
③接口:
SCLK:从master发过来的时钟信号 MOSI:master out slave in MISO:master in slave out SS: slave select (片选、从机选择)、有时候可以有多个SS,一般为0有效
④极性和相位
CPOL:决定开始和结束的条件;若设置为0,则开始为上升沿、结束为下降沿;若设置为1,则开始为下降沿、结束为上升沿;
CPHA:决定数据捕获以及数据改变的时序;若为0,master在下降沿改变数据,slave在上升沿采集数据;若为1,master在上升沿改变数据,slave在下降沿沿采集数据;
⑤两种片选SS的结构:多SS,以及SS译码
2.SPI架构及寄存器
- 架构
APB总线进行寄存器的配置,进而产生相应的SPI时序 - 寄存器:控制、状态、数据、扩展寄存器
①控制寄存器SPCR
- SPIE [7]:中断使能
- SPE [6]:工作使能
- MSTR[4]:固定为1
- CPOL[3]:极性
- CPHA[2]:相位
- SPR[1:0]:时钟速率选择
②状态寄存器SPSR
- SPIF[7]:中断上报
- WCOL[6]:写冲突
- WFFULL[3]:写fifo满
- WFEMPTY[2]:写fifo空
- RFFULL[1]:读fifo满
- RFEMPTY[0]:读fifo空
③数据寄存器SPDR
④扩展寄存器SPER
3.RTL代码
①题外话:寄存器配置的电路——使能则通,不使能则保持
spi_top.v
①端口:包括APB、SPI、中断上报
module spi_top
(
input wire PCLKG,
input wire PRESETn,
input wire [11:2] PADDR,
input wire PSEL,
input wire PENABLE,
input wire PWRITE,
input wire [31:0] PWDATA,
output wire [31:0] PRDATA,
output wire PREADY,
output wire PSLVERR,
output wire spi_int,//传输完成上报中断
output wire pad_spi_sck_out,
output wire pad_spi_sck_oen,
output wire pad_spi_do_out,//MOSI
output wire pad_spi_do_oen,
output wire pad_spi_cs0_out,
output wire pad_spi_cs0_oen,
output wire pad_spi_cs1_out,
output wire pad_spi_cs1_oen,
output wire pad_spi_di_in//MISO
);
②重定义:因为是用其他spi ip 改的,需要给APB定义一下,包括时钟复位等等
wire clk_i = PCLKG ;
wire rst i = PRESETn ;
wire [2:0] adr_i = PADDR[4:2] ;
wire we_i = PSEL & (~PENABLE) & PWRITE;
wire [7:0] dat_i = PYDATA[7:0] ;
wire miso_i= pad_spi_di_in ;
reg [7:0] dat_o;
reg inta_o;
reg sck_o;
wire mosi_o;
reg cs_sel; //0:cs0 1:cs1
③内部信号:寄存器信号定义、fifo、中断、使能等信号
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 rfre, rffull, rfempty;
wire [7:0] wfdout;
wire wfwe, wffull, wfempty;
reg [11:0]clkcnt;
wire ena = ~|clkcnt;
// 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;
//
// Wishbone interface
wire wb_acc = we_i;
wire wb_wr = we_i;
④APB数据补够32位,SPI接口重定义,oen信号默认0
assign PRDATA ={{24{1'b0}},dat_o} ;
assign PREADY = 1'bl;
assign PSLVERR = 1'bl;
assign spi_int = inta_o;
assign pad_spi_sck_out = sck_o;
assign pad spi_sck oen = 1'b0;
assign pad_spi_do_out = mosi_o;
assign pad spi do oen = 1'b0;
assign pad spi_cse_out = (~csel) & ena;
assign pad spi cs0_oen = 1'b0;
assign pad_spi_cs1_out = (cs_sel) & ena;
assign pad_spi_cs1_oen = 1'b0;
⑤根据地址和使能,给寄存器赋值(dat_i),再输出(dat_o)
// dat_i
always @(posedge clk_i or negedge rst_i)begin
if (~rst_i)begin
spcr <= #1 8'h10; // set master bit
sper <= #1 8'h00;
cs_sel <= 1'b0;
end
else if (wb_wr)begin
if (adr_i[1:0] == 2'b00)
spcr <= #1 dat_i | 8'h10; // always set master bit
if (adr_i[1:0] == 2'b11)
sper <= #1 dat_i;
if (adr_i[2] == 1'b0)
cs_sel <= 1'b0;
end
else begin
cs_sel <= 1'b1;
end
end
// dat_o
always @(posedge clk_i)
case(adr_i[1:0]) // synopsys full_case parallel_case
2'b00: dat_o <= #1 spcr;
2'b01: dat_o <= #1 spsr;
2'b10: dat_o <= #1 rfdout;
2'b11: dat_o <= #1 sper;
endcase
⑥fifo的使能与过载
// write fifo
assign wfwe = wb_acc & (adr_i[1:0] == 2'b10) & ack_o & we_i;
assign wfov = wfwe & wffull; //over
// read fifo
assign rfre = wb_acc & (adr_i[1:0] == 2'b10) & ~we_i;
⑦寄存器每个位的配置
// 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 = wb_wr & (adr_i[1:0] == 2'b01);
⑧中断
reg spif;
always @(posedge clk_i)
if (~spe)
spif <= #1 1'b0;
else
spif <= #1 (tirq | spif) & ~(wr_spsr & dat_i[7]); //产生中断并保持
reg wcol;
always @(posedge clk_i)
if (~spe)
wcol <= #1 1'b0;
else
wcol <= #1 (wfov | wcol) & ~(wr_spsr & dat_i[6]);//over 中断
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 clk_i)
inta_o <= #1 spif & spie;
⑨fifo例化(同步fifo)
// hookup read/write buffer fifo
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 )
);
⑩时钟分频:由espr寄存器决定
// generate clk divider
always @(posedge clk_i)
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'h3; // 16 -- original M68HC11 coding
4'b0011: clkcnt <= #1 12'hf; // 32 -- original M68HC11 coding
4'b0100: clkcnt <= #1 12'h1f; // 8
4'b0101: clkcnt <= #1 12'h7; // 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
?传输状态机:根据cpol、cpha决定极点和相位
// transfer statemachine
always @(posedge clk_i)
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 clk_i)
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
|