SPI Interface.
`timescale 1 ns / 1 ps module task_spi ( // SPI master in out ports input i_spi_miso , output o_spi_clk , output o_spi_csn , output o_spi_wex , output o_spi_mosi ); localparam SPI_PER = 300; reg spi_clk; reg spi_csn; reg spi_wex; reg spi_mosi; reg [7:0] sp
onlyiknowabout.tistory.com
`timescale 1 ns / 1 ps
module task_spi (
// SPI master in out ports
input i_spi_miso ,
output o_spi_clk ,
output o_spi_csn ,
output o_spi_wex ,
output o_spi_mosi
);
localparam SPI_PER = 300;
reg spi_clk;
reg spi_csn;
reg spi_wex;
reg spi_mosi;
reg [7:0] spi_data;
reg [7:0] spi_rdata;
integer count;
////////////////////////////////////SPI_TASK////////////////////////////////////
initial begin
spi_clk = 1'b1; spi_csn = 1'b1; spi_wex = 1'bx; // spi initial status
repeat(3) #(SPI_PER); // waveform delay : 900ns
SPI_WR_ADDR(8'h07);
SPI_WR_DATA(8'hff);
SPI_WR_DATA(8'hff);
SPI_WR_DATA(8'hff);
SPI_WR_DATA(8'hff);
SPI_WR_DATA(8'h09);
SPI_WR_DATA(8'hff);
SPI_WR_DATA(8'hff);
SPI_WR_DATA(8'hff);
SPI_WR_DATA(8'hff);
SPI_WR_DATA(8'd122);
SPI_WR_DEND(8'd200);
repeat(4) #(SPI_PER);
SPI_RD_ADDR(8'h07);
SPI_RD_DUMM;
SPI_RD_DATA;
SPI_RD_DATA;
SPI_RD_DATA;
SPI_RD_DATA;
SPI_RD_DATA;
SPI_RD_DATA;
SPI_RD_DATA;
SPI_RD_DATA;
SPI_RD_DATA;
SPI_RD_DATA;
SPI_RD_DEND;
repeat(4) #(SPI_PER); // waveform end delay
end
////////////////////////////////////////////////////////////////////////////////
task automatic SPI_WR_ADDR;
input [7:0] spi_addr;
begin
spi_clk = 1'b1; spi_csn = 1'b1; spi_wex = 1'bx;
#(SPI_PER);
spi_clk = 1'b0; spi_csn = 1'b0; spi_wex = 1'b0;
for (count=7; count>=0; count=count-1) begin
spi_mosi = spi_addr[7-count];
#(SPI_PER/2) spi_clk = 1'b1;
#(SPI_PER/2) spi_clk = 1'b0;
end
spi_data = spi_addr;
end
endtask
task automatic SPI_WR_DATA;
input [7:0] spi_wdata;
begin
for (count=7; count>=0; count=count-1) begin
spi_mosi = spi_wdata[7-count];
#(SPI_PER/2) spi_clk = 1'b1;
#(SPI_PER/2) spi_clk = 1'b0;
end
spi_data = spi_wdata;
end
endtask
task automatic SPI_WR_DEND;
input [7:0] spi_wdata;
begin
for (count=7; count>=0; count=count-1) begin
spi_mosi = spi_wdata[7-count];
#(SPI_PER/2) spi_clk = 1'b1;
#(SPI_PER/2) spi_clk = 1'b0;
end
spi_data = spi_wdata;
spi_clk = 1'b1; spi_csn = 1'b1; spi_wex = 1'bx; count = 'bx; spi_mosi = 1'bx;
end
endtask
task automatic SPI_RD_ADDR;
input [7:0] spi_addr;
begin
spi_clk = 1'b1; spi_csn = 1'b1; spi_wex = 1'bx;
#(SPI_PER);
spi_clk = 1'b0; spi_csn = 1'b0; spi_wex = 1'b1;
for (count=7; count>=0; count=count-1) begin
spi_mosi = spi_addr[7-count];
#(SPI_PER/2) spi_clk = 1'b1;
#(SPI_PER/2) spi_clk = 1'b0;
end
spi_data = spi_addr; spi_mosi = 1'bx;
end
endtask
task automatic SPI_RD_DUMM;
begin
for (count=7; count>=0; count=count-1) begin
spi_rdata[7-count] = i_spi_miso;
#(SPI_PER/2) spi_clk = 1'b1;
// spi_rdata[7-count] = i_spi_miso;
#(SPI_PER/2) spi_clk = 1'b0;
end
spi_data = spi_rdata;
end
endtask
task automatic SPI_RD_DATA;
begin
for (count=7; count>=0; count=count-1) begin
spi_rdata[7-count] = i_spi_miso;
#(SPI_PER/2) spi_clk = 1'b1;
// spi_rdata[7-count] = i_spi_miso;
#(SPI_PER/2) spi_clk = 1'b0;
end
spi_data = spi_rdata;
end
endtask
task automatic SPI_RD_DEND;
begin
for (count=7; count>=0; count=count-1) begin
spi_rdata[7-count] = i_spi_miso;
#(SPI_PER/2) spi_clk = 1'b1;
// spi_rdata[7-count] = i_spi_miso;
#(SPI_PER/2) spi_clk = 1'b0;
end
spi_data = spi_rdata;
spi_clk = 1'b1; spi_csn = 1'b1; spi_wex = 1'bx; count = 3'bx;
end
endtask
assign o_spi_clk = spi_clk ;
assign o_spi_csn = spi_csn ;
assign o_spi_wex = spi_wex ;
assign o_spi_mosi = spi_mosi;
endmodule
module spi_slave_fsm (
// system reset
input i_rstb ,
// spi slave clock & reset
input i_spi_clk ,
input i_spi_csn ,
input i_spi_wex ,
input i_spi_mosi ,
output reg o_spi_miso
);
// define parameter
localparam IDLE = 3'd0;
localparam WR_ADDR = 3'd1;
localparam WR_DATA = 3'd2;
localparam RD_ADDR = 3'd3;
localparam RD_DUMM = 3'd4;
localparam RD_DATA = 3'd5;
// internal register
reg [2:0] cst, nst, count;
reg w_wdata_ena, w_wr_ena;
reg w_rdata_ena, w_rd_ena;
reg [7:0] w_spi_waddr;
reg [7:0] w_spi_raddr;
reg [7:0] spi_mem[0:255];
reg [7:0] adr_count;
reg [7:0] r_spi_rdata;
// internal wire
wire w_spi_rst = i_spi_csn | ~i_rstb ;
wire w_enable = w_wr_ena | w_rd_ena ;
// state transition
always@(posedge i_spi_clk or posedge w_spi_rst)
if(w_spi_rst)
cst <= IDLE;
else
cst <= nst;
// bit counter logic
always@(posedge i_spi_clk or posedge w_spi_rst)
if(w_spi_rst)
count <= 3'd0;
else if(count == 3'd7)
count <= 3'd0;
else
count <= count + 3'd1;
// nst logic
always@* begin
nst = cst;
case(cst)
IDLE : nst = (~i_spi_csn) ? (~i_spi_wex) ? WR_ADDR : RD_ADDR : IDLE ;
WR_ADDR : nst = (count == 3'd0 & w_enable ) ? WR_DATA : WR_ADDR ;
WR_DATA : nst = (i_spi_wex ) ? IDLE : WR_DATA ;
RD_ADDR : nst = (count == 3'd0 & w_enable ) ? RD_DUMM : RD_ADDR ;
RD_DUMM : nst = (count == 3'd0 & w_enable ) ? RD_DATA : RD_DUMM ;
RD_DATA : nst = (~i_spi_wex) ? IDLE : RD_DATA ;
default : nst = IDLE ;
endcase
end
// write enable signal
always@(*) begin
case(cst)
WR_ADDR : begin w_wr_ena = 1'b1; w_wdata_ena = 1'b0; end
WR_DATA : begin w_wr_ena = 1'b1; w_wdata_ena = 1'b1; end
default : begin w_wr_ena = 1'b0; w_wdata_ena = 1'b0; end
endcase
end
// read enable signal
always@(*) begin
case(cst)
RD_ADDR : begin w_rd_ena = 1'b1; w_rdata_ena = 1'b0; end
RD_DUMM : begin w_rd_ena = 1'b1; w_rdata_ena = 1'b0; end
RD_DATA : begin w_rd_ena = 1'b1; w_rdata_ena = 1'b1; end
default : begin w_rd_ena = 1'b0; w_rdata_ena = 1'b0; end
endcase
end
// write address
always@ (posedge i_spi_clk or posedge w_spi_rst)
if(w_spi_rst)
w_spi_waddr <= 8'hx;
else
case(nst)
WR_ADDR : w_spi_waddr[count] <= i_spi_mosi ;
default : w_spi_waddr <= w_spi_waddr;
endcase
// read address
always@ (posedge i_spi_clk or posedge w_spi_rst)
if(w_spi_rst)
w_spi_raddr <= 8'hx;
else
case(nst)
RD_ADDR : w_spi_raddr[count] <= i_spi_mosi ;
default : w_spi_raddr <= w_spi_raddr;
endcase
//////////////////////////REGISTER TABLE//////////////////////////
// address counter
always@(posedge i_spi_clk or posedge w_spi_rst)
if(w_spi_rst)
adr_count <= 8'd0;
else if(count == 3'd7 & (w_wdata_ena | w_rdata_ena))
adr_count <= adr_count + 8'd1;
// mem write
always@(posedge i_spi_clk or posedge w_spi_rst)
if(w_spi_rst)
spi_mem[adr_count + w_spi_waddr][count] <= 1'bx;
else if(~i_spi_wex)
spi_mem[adr_count + w_spi_waddr][count] <= i_spi_mosi;
// mem read
always@(posedge i_spi_clk or posedge w_spi_rst)
if(w_spi_rst)
o_spi_miso <= 1'bx;
else if(i_spi_wex)
o_spi_miso <= spi_mem[adr_count + w_spi_raddr][count];
//////////////////////////////////////////////////////////////////
endmodule
`timescale 1 ns / 1 ps
module tb_spi_slave_fsm ();
wire w_spi_clk ;
wire w_spi_csn ;
wire w_spi_wex ;
wire w_spi_mosi ;
reg r_rstb ;
wire w_spi_miso ;
task_spi u_task_spi (
.i_spi_miso (w_spi_miso ),
.o_spi_clk (w_spi_clk ),
.o_spi_csn (w_spi_csn ),
.o_spi_wex (w_spi_wex ),
.o_spi_mosi (w_spi_mosi )
);
spi_slave_fsm u_spi_slave_fsm (
.i_rstb (r_rstb ),
.i_spi_clk (w_spi_clk ),
.i_spi_csn (w_spi_csn ),
.i_spi_wex (w_spi_wex ),
.i_spi_mosi (w_spi_mosi ),
.o_spi_miso (w_spi_miso )
);
initial begin
r_rstb = 1'b1;
#100 r_rstb = 1'b0;
#200 r_rstb = 1'b1;
end
localparam IDLE = 3'd0;
localparam WR_ADDR = 3'd1;
localparam WR_DATA = 3'd2;
localparam RD_ADDR = 3'd3;
localparam RD_DUMM = 3'd4;
localparam RD_DATA = 3'd5;
reg [7*8-1:0] cst;
always@(u_spi_slave_fsm.cst)begin
case(u_spi_slave_fsm.cst)
IDLE : cst = "IDLE ";
WR_ADDR : cst = "WR_ADDR";
WR_DATA : cst = "WR_DATA";
RD_ADDR : cst = "RD_ADDR";
RD_DUMM : cst = "RD_DUMM";
RD_DATA : cst = "RD_DATA";
default : cst = "IDLE ";
endcase
end
reg [7*8-1:0] nst;
always@(u_spi_slave_fsm.nst)begin
case(u_spi_slave_fsm.nst)
IDLE : nst = "IDLE ";
WR_ADDR : nst = "WR_ADDR";
WR_DATA : nst = "WR_DATA";
RD_ADDR : nst = "RD_ADDR";
RD_DUMM : nst = "RD_DUMM";
RD_DATA : nst = "RD_DATA";
default : nst = "IDLE ";
endcase
end
endmodule
vlib work
vlog tb_spi_slave_fsm.v spi_slave_fsm.v register_table.v task_spi.v
vsim work.tb_spi_slave_fsm
view wave
add wave -divider TASK_SPI
add wave -radix uns /w_spi_clk
add wave -radix uns /w_spi_csn
add wave -radix uns /w_spi_wex
add wave -radix uns /w_spi_mosi
add wave -radix uns /w_spi_miso
add wave -radix dec /u_task_spi/count
add wave -radix hex /u_task_spi/spi_data
add wave -divider SPI_SLAVE_FSM
add wave -radix uns /u_spi_slave_fsm/w_spi_rst
add wave -radix uns /u_spi_slave_fsm/i_spi_clk
add wave -radix uns /u_spi_slave_fsm/i_spi_csn
add wave -radix uns /u_spi_slave_fsm/i_spi_wex
add wave -radix uns /u_spi_slave_fsm/cst
add wave -radix ASCII /cst
add wave -radix uns /u_spi_slave_fsm/count
add wave -radix uns /u_spi_slave_fsm/w_spi_waddr
add wave -radix uns /u_spi_slave_fsm/w_spi_raddr
add wave -group enable -radix uns /u_spi_slave_fsm/w_wr_ena
add wave -group enable -radix uns /u_spi_slave_fsm/w_wdata_ena
add wave -group enable -radix uns /u_spi_slave_fsm/w_rd_ena
add wave -group enable -radix uns /u_spi_slave_fsm/w_rdata_ena
add wave -radix uns /u_spi_slave_fsm/r_spi_rdata
add wave -divider REGISTER_TABLE
add wave -radix uns /u_spi_slave_fsm/count
add wave -radix uns /u_spi_slave_fsm/adr_count
add wave -radix uns /u_spi_slave_fsm/spi_mem
run 66000ns
'Logic Design' 카테고리의 다른 글
AXI4 Bus Interface (0) | 2024.07.16 |
---|---|
Pulse Generator. (0) | 2024.03.24 |
sys_sync_gen (0) | 2024.03.01 |
Image processing filter design. (0) | 2024.02.09 |
MIPI specification. (0) | 2024.02.01 |