关于STM32与FPGA的通信(SPI通信方式)(2-实现MCU对FPGA的写入-写入地址)

 单片机新增函数,用于写入数据(使能信号,数据帧),第一个函数用于测试写入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_DOUTW_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满足要求,没出问题

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值