单片机新增函数,用于写入数据(使能信号,数据帧),第一个函数用于测试写入RAM(目前并未再FPGA中完全模块化相应实例,待下一步完成)
第二个函数就是写入对应地址的计数器(目前并未再FPGA中完全模块化相应实例,待下一步完成)
void spi_wr_addr_16b_data_16b_ram_128(){
int i = 0, wa = 0, wd = 0, wa_H, wa_L, wd_H, wd_L;
// test case 1
for (i = 0 ; i < 128; i ++){
wa = i&(128-1); wa_H = (wa >> 8) & 0xff; wa_L = wa & 0xff;
wd = wa*255 ; wd_H = (wd >> 8) & 0xff; wd_L = wd & 0xff;
// ADDRESS MUST BE WRITE FIRST
// A_H A_L , D_H , D_L
spi_wr_32bit_MSB_first(0x00, 0x05, wa_H, wa_L); // write wa reg
spi_wr_32bit_MSB_first(0x00, 0x06, wd_H, wd_L); // write wd reg
}
// Delay(1000*1000*10);
// test case 2
for (i = 0 ; i < 128; i ++){
wa = i&(128-1) ; wa_H = (wa >> 8) & 0xff; wa_L = wa & 0xff;
wd = (128-1-wa)*255 ; wd_H = (wd >> 8) & 0xff; wd_L = wd & 0xff;
// ADDRESS MUST BE WRITE FIRST
// A_H A_L , D_H , D_L
spi_wr_32bit_MSB_first(0x00, 0x05, wa_H, wa_L); // write wa reg
spi_wr_32bit_MSB_first(0x00, 0x06, wd_H, wd_L); // write wd reg
}
}
void spi_wr_addr_16b_data_16b_loop(){
while(1) {
// counter increase value write test case
// A_H A_L , D_H , D_L
spi_wr_32bit_MSB_first(0x00, 0x00, 0x02, 0x02); // 16 bit counter 1
spi_wr_32bit_MSB_first(0x00, 0x01, 0x01, 0x01); // 16 bit counter 2
spi_wr_32bit_MSB_first(0x00, 0x00, 0x04, 0x04); // 16 bit counter 1
spi_wr_32bit_MSB_first(0x00, 0x01, 0x05, 0x05); // 16 bit counter 2
spi_wr_32bit_MSB_first(0x00, 0x02, 0x01, 0x02); // 32 bit counter L
spi_wr_32bit_MSB_first(0x00, 0x03, 0x03, 0x04); // 32 bit counter H
spi_wr_32bit_MSB_first(0x00, 0x04, 0x00, 0x00); // 32 bit counter Update
spi_wr_32bit_MSB_first(0x00, 0x02, 0x04, 0x03); // 32 bit counter L
spi_wr_32bit_MSB_first(0x00, 0x03, 0x02, 0x01); // 32 bit counter H
spi_wr_32bit_MSB_first(0x00, 0x04, 0x00, 0x00); // 32 bit counter Update
// ram write test case
spi_wr_addr_16b_data_16b_ram_128();
}
}
MCU部分较简略,FPGA是改动的大头,现在只是中间过程,后续第三步完成整个通信的时序
接下来的是子模块地址译码器
这段代码接收输入数据,输入使能,输入地址,然后输出数据,输出数据标志位,输出一个TESTOUT。选哟注意一下这边信号位宽,有点绕。 I_DIN 是这个模块中的数据信号;
I_AIN作为地址的输入信号,提前拆分开方便后续操作。
O_DAT输出的数据。O_DOV是输出的标志位,32位是为了寄存器寻址。
module addr_decode(
I_CLK ,
I_DIN , // input data
I_ENA , // input enable
I_AIN , // input address
O_DAT , // output data, all address
O_DOV , // output data valid, all address
O_TESTOUT1);
parameter NADR = 32 ; // num of address
parameter DW = 16 ; // data width
input I_CLK ;
input [DW -1:0] I_DIN ;
input [32-DW-1:0] I_AIN ; // address + data = 32 bit width
input I_ENA ;
output[DW -1:0] O_DAT ;
output[NADR -1:0] O_DOV ;
output O_TESTOUT1;
// O_DAT |R1_I_DIN|I_DIN
// O_DOV |R1_I_AIN|I_AIN
// R2_I_ENA |R1_I_ENA|I_ENA
// W_to_DOV |
reg [DW -1:0] R1_I_DIN, O_DAT;
reg [32-DW-1:0] R1_I_AIN;
reg R1_I_ENA, R2_I_ENA;
reg [NADR -1:0] O_DOV, W_to_DOV;
always @ (posedge I_CLK) begin
R1_I_DIN <= I_DIN ;
O_DAT <= R1_I_DIN;
R1_I_AIN <= I_AIN ;
R1_I_ENA <= I_ENA ;
R2_I_ENA <= R1_I_ENA;
O_DOV <= W_to_DOV;
end
always @ (*) begin
case(R1_I_AIN)
16'h00000 : W_to_DOV = 32'h0000_0001& {32{R2_I_ENA}};
16'h00001 : W_to_DOV = 32'h0000_0002& {32{R2_I_ENA}};
16'h00002 : W_to_DOV = 32'h0000_0004& {32{R2_I_ENA}};
16'h00003 : W_to_DOV = 32'h0000_0008& {32{R2_I_ENA}};
16'h00004 : W_to_DOV = 32'h0000_0010& {32{R2_I_ENA}};
16'h00005 : W_to_DOV = 32'h0000_0020& {32{R2_I_ENA}};
16'h00006 : W_to_DOV = 32'h0000_0040& {32{R2_I_ENA}};
16'h00007 : W_to_DOV = 32'h0000_0080& {32{R2_I_ENA}};
16'h00008 : W_to_DOV = 32'h0000_0100& {32{R2_I_ENA}};
16'h00009 : W_to_DOV = 32'h0000_0200& {32{R2_I_ENA}};
16'h0000A : W_to_DOV = 32'h0000_0400& {32{R2_I_ENA}};
16'h0000B : W_to_DOV = 32'h0000_0800& {32{R2_I_ENA}};
16'h0000C : W_to_DOV = 32'h0000_1000& {32{R2_I_ENA}};
16'h0000D : W_to_DOV = 32'h0000_2000& {32{R2_I_ENA}};
16'h0000E : W_to_DOV = 32'h0000_4000& {32{R2_I_ENA}};
16'h0000F : W_to_DOV = 32'h0000_8000& {32{R2_I_ENA}};
16'h00010 : W_to_DOV = 32'h0001_0000& {32{R2_I_ENA}};
16'h00011 : W_to_DOV = 32'h0002_0000& {32{R2_I_ENA}};
16'h00012 : W_to_DOV = 32'h0004_0000& {32{R2_I_ENA}};
16'h00013 : W_to_DOV = 32'h0008_0000& {32{R2_I_ENA}};
16'h00014 : W_to_DOV = 32'h0010_0000& {32{R2_I_ENA}};
16'h00015 : W_to_DOV = 32'h0020_0000& {32{R2_I_ENA}};
16'h00016 : W_to_DOV = 32'h0040_0000& {32{R2_I_ENA}};
16'h00017 : W_to_DOV = 32'h0080_0000& {32{R2_I_ENA}};
16'h00018 : W_to_DOV = 32'h0100_0000& {32{R2_I_ENA}};
16'h00019 : W_to_DOV = 32'h0200_0000& {32{R2_I_ENA}};
16'h0001A : W_to_DOV = 32'h0400_0000& {32{R2_I_ENA}};
16'h0001B : W_to_DOV = 32'h0800_0000& {32{R2_I_ENA}};
16'h0001C : W_to_DOV = 32'h1000_0000& {32{R2_I_ENA}};
16'h0001D : W_to_DOV = 32'h2000_0000& {32{R2_I_ENA}};
16'h0001E : W_to_DOV = 32'h4000_0000& {32{R2_I_ENA}};
16'h0001F : W_to_DOV = 32'h8000_0000& {32{R2_I_ENA}};
default: W_to_DOV = 8'b 0000_0000 & {8{R2_I_ENA}}; // this should NOT occur
endcase
end
endmodule
然后是之前的传入并出模块
module spi_in_to_parallel_data_out(
I_CLK ,
I_CS ,
I_SCLK ,
I_SDIN ,
O_DOUT ,
O_DOV ,
O_TESTOUT1);
input I_CLK ;
input I_CS ;
input I_SCLK ;
input I_SDIN ;
output O_TESTOUT1;
reg R1_I_CS ,R2_I_CS ,R3_I_CS ;
reg R1_I_SCLK ,R2_I_SCLK,R3_I_SCLK ;
reg R1_I_SDIN ,R2_I_SDIN ;
reg W_I_CS_up , W_I_CS_down, W_I_SCLK_up;
reg [32-1:0] R_sdin_shift, R_out_buf;
output [32-1:0] O_DOUT;
output O_DOV ;
reg O_DOV ;
// pipeline
// | meta stability
// R3_I_CS |R2_I_CS |R1_I_CS | I_CS <-- Input
// R3_I_SCLK |R2_I_SCLK |R1_I_SCLK | I_SCLK
// |R2_I_SDIN |R1_I_SDIN | I_SDIN
// |W_I_CS_up
// |W_I_CS_down
// |W_I_SCLK_up
always @ (*) begin
W_I_CS_up = ((R3_I_CS == 1'b0)&&( R2_I_CS == 1'b1)) ? 1'b1 : 1'b0;
W_I_CS_down = ((R3_I_CS == 1'b1)&&( R2_I_CS == 1'b0)) ? 1'b1 : 1'b0;
W_I_SCLK_up = ((R3_I_SCLK == 1'b0)&&( R2_I_SCLK == 1'b1)) ? 1'b1 : 1'b0;
end
always @ (posedge I_CLK) begin
R1_I_CS <= I_CS ;
R1_I_SCLK <= I_SCLK ;
R1_I_SDIN <= I_SDIN ;
R2_I_CS <= R1_I_CS ;
R2_I_SCLK <= R1_I_SCLK ;
R2_I_SDIN <= R1_I_SDIN ;
R3_I_CS <= R2_I_CS ;
R3_I_SCLK <= R2_I_SCLK ;
end
always @ (posedge I_CLK) begin
if(W_I_CS_down)
R_sdin_shift <= 0;
else
if(W_I_SCLK_up)
R_sdin_shift <= {R_sdin_shift[30:0],R2_I_SDIN};
if(W_I_CS_up)begin
R_out_buf <= R_sdin_shift;
O_DOV <= W_I_CS_up;
end
end
assign O_DOUT = R_out_buf;
assign O_TESTOUT1 = W_I_SCLK_up ;
endmodule
顶层模块的代码,连连看代码,当然可以直接用原理图连,更加直观,但不易传播。
首先是串入并出模块,需要注意的是W_U_spi_O_DOUT,W_U_spi_O_DOV,这是两个内部信号,等会要传给地址译码器的。那现在我们知道了,这个模块负责输出的是标志位和MCU发送过来的数据。
再看到地址译码器模块时钟还是同一个,保证同步时序。
使能就是之前提到的内部信号W_U_spi_O_DOV了
然后传入的数据帧构成是前16位作为地址,后16位作为数据的
输出两个内部信号assign一下而已,不多说了
module spi_bus(
I_CLK ,
I_CS ,
I_SCLK ,
I_SDIN ,
O_WD ,
O_WE ,
O_TESTOUT1);
input I_CLK ;
input I_CS ;
input I_SCLK ;
input I_SDIN ;
output [16-1:0] O_WD ;
output [32-1:0] O_WE ;
output O_TESTOUT1;
wire[32-1:0] W_U_spi_O_DOUT;
wire W_U_spi_O_DOV ;
spi_in_to_parallel_data_out U_spi_s2p(
.I_CLK (I_CLK ),
.I_CS (I_CS ),
.I_SCLK (I_SCLK),
.I_SDIN (I_SDIN),
.O_DOUT (W_U_spi_O_DOUT),
.O_DOV (W_U_spi_O_DOV ),
.O_TESTOUT1());
wire [16-1:0] W_U_dec_O_DAT;
wire [32 -1:0] W_U_dec_O_DOV;
addr_decode U_decode(
.I_CLK (I_CLK ),
.I_ENA (W_U_spi_O_DOV ), // input enable
.I_DIN (W_U_spi_O_DOUT[15: 0]),
.I_AIN (W_U_spi_O_DOUT[31:16]),
.O_DAT (W_U_dec_O_DAT ),
.O_DOV (W_U_dec_O_DOV ),
.O_TESTOUT1 ());
assign O_WD = W_U_dec_O_DAT;
assign O_WE = W_U_dec_O_DOV;
endmodule
SignalTap满足要求,没出问题