863|2

58

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

【国产Tang Primer 25K测评】一次失败的尝试中出现的问题-SD卡+SDRAM通过HDMI显示 [复制链接]

 
  1. 最近初步看懂了nestang25k的例程,依旧没下手,看见里面出现了SD卡和SDRAM的内容,于是我就去野火的例程里搜出来了一个最接近的例程想移植看看,区别在于野火使用的是DDR3 SDRAM,但是tang25k使用的SDRAM型号貌似是DDR1 SDRAM,这里没有DDR3的话只能进行更改,然后我就想起来了tang25k的活动页介绍里有提到DDR3
    emmmm,真的有DDR3吗?而且也并没有参考例程存在,难道SDRAM模块也可以当作DDR3用?感觉上是写错了,或者需要自行设计一个DDR3模块。
     
  2. 延续上面的思路,没有DDR3,就用普通的SDRAM应该也一样,这里考虑使用nestang25k例程中给出的sdram控制器或者高云研制的sdram的ip进行替换。
  3. 然后就是SD卡,nestang25k例程里用到的SD卡是SDIO模式,野火的SD卡用的是SPI模式,这里我想使用野火的SPI模式,因为人家例程都是设计好的,尽量减少更改也能简单些。
  4. 开始写代码:
    1. 碰到的第一个问题是 按键问题,PLL的复位按键时灵时不灵让我很恼火,想研究一下到底为什么,花了半天时间,我发现了问题,在引脚绑定中,要根据按键实际的物理接线方式,对引脚约束为上拉下拉,否则按键失灵(一直忽略引脚约束这个问题是因为我一直认为只要物理结构上设置了上拉下拉就不用引脚约束了,实际是错误的)。
          以下是我测试按键的代码:
      module led_top(
          input   CLK_0,
          input s1,
          input reset,//系统复位,高电平有效
      
          input   button1,
          input   button2,
          input   button3,
          input   button4,
          output  IO_voltage,
          output  led1,
          output  led2,
          output  led3,
          output  led4,
          output  led5,
          output  led6,
          output  led7
      );
      
      //parameter   Clock_frequency =   50_000_000;// 50MHZ
      parameter   count_value     = 25_000_000;// count 
      reg[31:0]   count_value_reg;
      reg            count_value_flag;
      wire    CLK_1;
      always@(posedge CLK_1)begin   
          if(count_value_reg  <=count_value)begin
              count_value_reg <=count_value_reg+1'b1;
              count_value_flag<=1'b0;
          end
          else begin
              count_value_reg<=32'b0;
              count_value_flag <=1'b1;//convert
          end
         end
      
      reg IO_voltage_reg=1'b0;
      
      always@(posedge CLK_1)begin
          if(count_value_flag)
              IO_voltage_reg<=~IO_voltage_reg;
          else
              IO_voltage_reg<=IO_voltage_reg;
      
      end
      
      assign  IO_voltage  =   IO_voltage_reg;
      wire    lock_o;
      
      board_input board_input_u1(
          .rst(s1),
          .clk(CLK_1),
          .p8_i(button1),
          .to_o({led6,led5})
      );
      
      wire CLK_25M;
      wire    CLK_8M388;
      
      assign led7=button2;//不按的时候为1,按下去为0,因为led模块设计原因,led=1,输出为0,led为0,输出为1
          Gowin_PLL your_instance_name(
              .lock(lock_o), //output lock,
              .clkout0(CLK_1), //output clkout0,50MHz,相偏180
              .clkout1(CLK_25M), //output clkout1,25MHZ
              .clkin(CLK_0), //input clkin,50MHz
              .clkout2(CLK_8M388), //output clkout2
              // .reset(1'b0) //input reset,没有进入复位状态,有输出;
              // .reset(1'b1) //input reset,没有进入复位状态,没输出;
              .reset(reset) //input reset,没有进入复位状态,初始取反为1,按前两行得出结论应该没有输出,实际上却有输出
      
          );
      
      endmodule
      
      
      module board_input(
      						rst,//复位
      						clk,//第八个按键,用来制作选择高中低
      						p8_i,
      						to_o//输出第八位的数据
      						);
      //			
      parameter    s1=2'b01,//1
      			 s2=2'b10,//2
      			 s3=2'b11;//3
      		
      //input rst;
      input rst;
      input clk;
      input p8_i;
      
      output reg [1:0] to_o;
      
      always@(posedge clk or posedge rst)
      begin
         if(rst)begin
      		to_o<=s3;
      	end
      	else if(~p8_i)
      		begin
      		 to_o<=s1;
      		end
      	else if(p8_i)
      		begin
      		 to_o<=s2;
      		end
      	else;
      end											
      		endmodule
      		
      

      以下为引脚约束:一定要设置引脚上拉下拉

      IO_LOC "led7" E11;
      IO_PORT "led7" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
      IO_LOC "led6" A10;
      IO_PORT "led6" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
      IO_LOC "led5" A11;
      IO_PORT "led5" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
      IO_LOC "led4" L11;
      IO_PORT "led4" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
      IO_LOC "led3" K11;
      IO_PORT "led3" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
      IO_LOC "led2" K5;
      IO_PORT "led2" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
      IO_LOC "led1" L5;
      IO_PORT "led1" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
      IO_LOC "IO_voltage" E10;
      IO_PORT "IO_voltage" PULL_MODE=DOWN DRIVE=8 BANK_VCCIO=3.3;
      IO_LOC "button4" G11;
      IO_PORT "button4" PULL_MODE=UP DRIVE=OFF BANK_VCCIO=3.3;
      IO_LOC "button3" D11;
      IO_PORT "button3" PULL_MODE=UP DRIVE=OFF BANK_VCCIO=3.3;
      IO_LOC "button2" B5510;
      IO_PORT "button2" PULL_MODE=UP DRIVE=OFF BANK_VCCIO=3.3;
      IO_LOC "button1" C10;
      IO_PORT "button1" PULL_MODE=UP DRIVE=OFF BANK_VCCIO=3.3;
      IO_LOC "reset" H10;
      IO_PORT "reset" PULL_MODE=DOWN DRIVE=OFF BANK_VCCIO=3.3;
      IO_LOC "CLK_0" E2;
      IO_PORT "CLK_0" PULL_MODE=UP DRIVE=OFF BANK_VCCIO=3.3;
      IO_LOC "s1" H11;
      IO_PORT "s1" PULL_MODE=DOWN DRIVE=OFF BANK_VCCIO=3.3;
      
    2. 发现的第二个问题就是 PMOD模块中的LED模块,起因是我在调试按键的时候,led灯的亮灭总是跟我想的反着来,搞得我糊涂,查看了一下原理图,发现原理图中FPGA引脚对应的是LED灯的阴极,也就是LED灯亮的时候,FPGA引脚输出低电平;所以为了方便,我推荐用 语句 LED=~FPGA_Pin;的方式进行输出。
       
    3. 第三个问题依旧是在调试按键的时候发现的,PMOD中的按键模块,我在进行对其的引脚绑定的时候,似乎总是会出错,一开始我以为是因为官方画板的时候把丝印放错了,后来发现是我看反了,在对PMOD端口和PMOD模块进行绑定的时候,应该按如下的视角进行匹配:
       
    4. SD卡控制模块的失效:首先体现了在了初始化模块的时候就已经失效了,表现在初始化信号没有反应,偶尔初始化模块有反应了,SD卡的读写模块也依旧没反应。排查了一下发现居然卡在了一个cnt计数的寄存器always里,看了很久的逻辑也没整明白为什么会卡在那里。
      偶然发现,当我对SD卡控制模块的复位接口进行胡乱接,或者在逻辑卡住的位置的代码段里加上一个根本没有用处的寄存器flag并赋值,然后外接到顶层模块接到LED显示,就会出现SD卡的初始化模块莫名其妙可以运行了,当然整体依旧不行。于是我开始怀疑起是不是我的SPI引脚约束有问题,一顿改之后依旧没有解决问题。按道理来说,SD卡控制模块已经是经过多次验证过的模块不会出问题,结合我上面讲到的偶尔初始化莫名成功的现象,有可能25k这种板上的SD卡PMOD模块不支持SPI模式?或者是在使用SPI模式的时候,50MHz的时钟,通道串扰会更加严重,PMOD模块设计上有问题?另外,官方的SD卡模块似乎并没有设计上拉电阻,这会不会有问题呢?
       
      以下是SD卡模块模块,读写模块,初始化模块代码,看不出什么问题
      `timescale  1ns/1ns
      ////////////////////////////////////////////////////////////////////////
      // Author        : EmbedFire
      //Create Date    : 2019/09/03
      // Module Name   : sd_ctrl
      // Project Name  : sd_vga_pic
      // Target Devices: Xilinx XC6SLX16
      // Tool Versions : ISE 14.7
      // Description   : SD卡控制顶层模块
      //
      ////////////////////////////////////////////////////////////////////////
      
      module  sd_ctrl
      (
          input   wire            sys_clk         ,   //输入工作时钟,频率50MHz
          input   wire            sys_clk_shift   ,   //输入工作时钟,频率50MHz,相位偏移90度
          input   wire            sys_rst_n       ,   //输入复位信号,低电平有效
          //SD卡接口
          input   wire            sd_miso         ,   //主输入从输出信号
          output  wire            sd_clk          ,   //SD卡时钟信号
          output  reg             sd_cs_n         ,   //片选信号
          output  reg             sd_mosi         ,   //主输出从输入信号
          //写SD卡接口
          input   wire            wr_en           ,   //数据写使能信号
          input   wire    [31:0]  wr_addr         ,   //写数据扇区地址
          input   wire    [15:0]  wr_data         ,   //写数据
          output  wire            wr_busy         ,   //写操作忙信号
          output  wire            wr_req          ,   //写数据请求信号
          //读SD卡接口
          input   wire            rd_en           ,   //数据读使能信号
          input   wire    [31:0]  rd_addr         ,   //读数据扇区地址
          output  wire            rd_busy         ,   //读操作忙信号
          output  wire            rd_data_en      ,   //读数据标志信号
          output  wire    [15:0]  rd_data         ,   //读数据
      
          output  wire            init_end            //SD卡初始化完成信号
      );
      
      //********************************************************************//
      //****************** Parameter and Internal Signal *******************//
      //********************************************************************//
      //wire define
      wire            init_cs_n   ;   //初始化阶段片选信号
      wire            init_mosi   ;   //初始化阶段主输出从输入信号
      wire            wr_cs_n     ;   //写数据阶段片选信号
      wire            wr_mosi     ;   //写数据阶段主输出从输入信号
      wire            rd_cs_n     ;   //读数据阶段片选信号
      wire            rd_mosi     ;   //读数据阶段主输出从输入信号
      
      //********************************************************************//
      //***************************** Main Code ****************************//
      //********************************************************************//
      //sd_clk:SD卡时钟信号
      assign  sd_clk = sys_clk_shift;
      
      //SD卡接口信号选择
      always@(*)
          if(init_end == 1'b0)
              begin
                  sd_cs_n <=  init_cs_n;
                  sd_mosi <=  init_mosi;
              end
          else    if(wr_busy == 1'b1)
              begin
                  sd_cs_n <=  wr_cs_n;
                  sd_mosi <=  wr_mosi;
              end
          else    if(rd_busy == 1'b1)
              begin
                  sd_cs_n <= rd_cs_n;
                  sd_mosi <= rd_mosi;
              end
          else
              begin
                  sd_cs_n <=  1'b1;
                  sd_mosi <=  1'b1;
              end
      
      //********************************************************************//
      //************************** Instantiation ***************************//
      //********************************************************************//
      //------------- sd_init_inst -------------
      sd_init sd_init_inst
      (
          .sys_clk        (sys_clk        ),  //输入工作时钟,频率50MHz
          .sys_clk_shift  (sys_clk_shift  ),  //输入工作时钟,频率50MHz,相位偏移90度
          .sys_rst_n      (sys_rst_n      ),  //输入复位信号,低电平有效
          .miso           (sd_miso        ),  //主输入从输出信号
      
          .cs_n           (init_cs_n      ),  //输出片选信号
          .mosi           (init_mosi      ),  //主输出从输入信号
          .init_end       (init_end       )   //初始化完成信号
      );
      
      //------------- sd_write_inst -------------
      sd_write    sd_write_inst
      (
          .sys_clk        (sys_clk            ),  //输入工作时钟,频率50MHz
          .sys_clk_shift  (sys_clk_shift      ),  //输入工作时钟,频率50MHz,相位偏移90度
          .sys_rst_n      (sys_rst_n          ),  //输入复位信号,低电平有效
          .miso           (sd_miso            ),  //主输入从输出信号
          .wr_en          (wr_en && init_end  ),  //数据写使能信号
          .wr_addr        (wr_addr            ),  //写数据扇区地址
          .wr_data        (wr_data            ),  //写数据
      
          .cs_n           (wr_cs_n            ),  //输出片选信号
          .mosi           (wr_mosi            ),  //主输出从输入信号
          .wr_busy        (wr_busy            ),  //写操作忙信号
          .wr_req         (wr_req             )   //写数据请求信号
      );
      
      //------------- sd_read_inst -------------
      sd_read sd_read_inst
      (
          .sys_clk        (sys_clk            ),  //输入工作时钟,频率50MHz
          .sys_clk_shift  (sys_clk_shift      ),  //输入工作时钟,频率50MHz,相位偏移90度
          .sys_rst_n      (sys_rst_n          ),  //输入复位信号,低电平有效
          .miso           (sd_miso            ),  //主输入从输出信号
          .rd_en          (rd_en & init_end   ),  //数据读使能信号
          .rd_addr        (rd_addr            ),  //读数据扇区地址
      
          .rd_busy        (rd_busy            ),  //读操作忙信号
          .rd_data_en     (rd_data_en         ),  //读数据标志信号
          .rd_data        (rd_data            ),  //读数据
          .cs_n           (rd_cs_n            ),  //片选信号
          .mosi           (rd_mosi            )   //主输出从输入信号
      );
      
      endmodule
      
      module  sd_init
      (
          input   wire    sys_clk         ,   //输入工作时钟,频率50MHz
          input   wire    sys_clk_shift   ,   //输入工作时钟,频率50MHz,相位偏移90度
          input   wire    sys_rst_n       ,   //输入复位信号,低电平有效
          input   wire    miso            ,   //主输入从输出信号
      
          output  reg     cs_n            ,   //输出片选信号
          output  reg     mosi            ,   //主输出从输入信号
          output  reg     init_end            //初始化完成信号
      );
      
      //********************************************************************//
      //****************** Parameter and Internal Signal *******************//
      //********************************************************************//
      //parameter define
      parameter   CMD0    =   {8'h40,8'h00,8'h00,8'h00,8'h00,8'h95},  //复位指令
                  CMD8    =   {8'h48,8'h00,8'h00,8'h01,8'haa,8'h87},  //查询电压指令
                  CMD55   =   {8'h77,8'h00,8'h00,8'h00,8'h00,8'hff},  //应用指令告知指令
                  ACMD41  =   {8'h69,8'h40,8'h00,8'h00,8'h00,8'hff};  //应用指令
      parameter   CNT_WAIT_MAX    =   8'd100; //上电后同步过程等待时钟计数最大值
      parameter   IDLE        =   4'b0000,    //初始状态
                  SEND_CMD0   =   4'b0001,    //CMD0发送状态
                  CMD0_ACK    =   4'b0011,    //CMD0响应状态
                  SEND_CMD8   =   4'b0010,    //CMD8发送状态
                  CMD8_ACK    =   4'b0110,    //CMD8响应状态
                  SEND_CMD55  =   4'b0111,    //CMD55发送状态
                  CMD55_ACK   =   4'b0101,    //CMD55响应状态
                  SEND_ACMD41 =   4'b0100,    //ACMD41发送状态
                  ACMD41_ACK  =   4'b1100,    //ACMD41响应状态
                  INIT_END    =   4'b1101;    //初始化完成状态
      
      //reg   define
      reg     [7:0]   cnt_wait        ;   //上电同步时钟计数器
      reg     [3:0]   state           ;   //状态机状态
      reg     [7:0]   cnt_cmd_bit     ;   //指令比特计数器
      reg             miso_dly        ;   //主输入从输出信号打一拍
      reg             ack_en          ;   //响应使能信号
      reg     [39:0]  ack_data        ;   //响应数据
      reg     [7:0]   cnt_ack_bit     ;   //响应数据字节计数
      
      //********************************************************************//
      //***************************** Main Code ****************************//
      //********************************************************************//
      //cnt_wait:上电同步时钟计数器
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              cnt_wait    <=  8'd0;
          else    if(cnt_wait >= CNT_WAIT_MAX)
              cnt_wait    <=  CNT_WAIT_MAX;
          else
              cnt_wait    <=  cnt_wait + 1'b1;
      
      //state:状态机状态跳转
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              state   <=  IDLE;
          else
              case(state)
                  IDLE:
                      if(cnt_wait == CNT_WAIT_MAX)
                          state   <=  SEND_CMD0;
                      else
                          state   <=  state;
                  SEND_CMD0:
                      if(cnt_cmd_bit == 8'd48)
                          state   <=  CMD0_ACK;
                      else
                          state   <=  state;
                  CMD0_ACK:
                      if(cnt_ack_bit == 8'd48)
                          if(ack_data[39:32] == 8'h01)
                              state   <=  SEND_CMD8;
                          else
                              state   <=  SEND_CMD0;
                      else
                          state   <=  state;
                  SEND_CMD8:
                      if(cnt_cmd_bit == 8'd48)
                          state   <=  CMD8_ACK;
                      else
                          state   <=  state;
                  CMD8_ACK:
                      if(cnt_ack_bit == 8'd48)
                          if(ack_data[11:8] == 4'b0001)
                              state   <=  SEND_CMD55;
                          else
                              state   <=  SEND_CMD8;
                      else
                          state   <=  state;
                  SEND_CMD55:
                      if(cnt_cmd_bit == 8'd48)
                          state   <=  CMD55_ACK;
                      else
                          state   <=  state;
                  CMD55_ACK:
                      if(cnt_ack_bit == 8'd48)
                          if(ack_data[39:32] == 8'h01)
                              state   <=  SEND_ACMD41;
                          else
                              state   <=  SEND_CMD55;
                      else
                          state   <=  state;
                  SEND_ACMD41:
                      if(cnt_cmd_bit == 8'd48)
                          state   <=  ACMD41_ACK;
                      else
                          state   <=  state;
                  ACMD41_ACK:
                      if(cnt_ack_bit == 8'd48)
                          if(ack_data[39:32] == 8'h00)
                              state   <=  INIT_END;
                          else
                              state   <=  SEND_CMD55;
                      else
                          state   <=  state;
                  INIT_END:
                      state   <=  state;
                  default:
                      state   <=  IDLE;
              endcase
      
      //cs_n,mosi,init_end,cnt_cmd_bit
      //片选信号,主输出从输入信号,初始化结束信号,指令比特计数器
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              begin
                  cs_n            <=  1'b1;
                  mosi            <=  1'b1;
                  init_end        <=  1'b0;
                  cnt_cmd_bit     <=  8'd0;
              end
          else
              case(state)
                  IDLE:
                      begin
                          cs_n            <=  1'b1;
                          mosi            <=  1'b1;
                          init_end        <=  1'b0;
                          cnt_cmd_bit     <=  8'd0;
                      end
                  SEND_CMD0:
                      if(cnt_cmd_bit == 8'd48)
                          cnt_cmd_bit     <=  8'd0;
                      else
                          begin
                              cs_n            <=  1'b0;
                              mosi            <=  CMD0[8'd47 - cnt_cmd_bit];
                              init_end        <=  1'b0;
                              cnt_cmd_bit     <=  cnt_cmd_bit + 8'd1;
                          end
                  CMD0_ACK:
                      if(cnt_ack_bit == 8'd47)
                          cs_n    <=  1'b1;
                      else
                          cs_n    <=  1'b0;
                  SEND_CMD8:
                      if(cnt_cmd_bit == 8'd48)
                          cnt_cmd_bit     <=  8'd0;
                      else
                          begin
                              cs_n            <=  1'b0;
                              mosi            <=  CMD8[8'd47 - cnt_cmd_bit];
                              init_end        <=  1'b0;
                              cnt_cmd_bit     <=  cnt_cmd_bit + 8'd1;
                          end
                  CMD8_ACK:
                      if(cnt_ack_bit == 8'd47)
                          cs_n    <=  1'b1;
                      else
                          cs_n    <=  1'b0;
                  SEND_CMD55:
                      if(cnt_cmd_bit == 8'd48)
                          cnt_cmd_bit     <=  8'd0;
                      else
                          begin
                              cs_n            <=  1'b0;
                              mosi            <=  CMD55[8'd47 - cnt_cmd_bit];
                              init_end        <=  1'b0;
                              cnt_cmd_bit     <=  cnt_cmd_bit + 8'd1;
                          end
                  CMD55_ACK:
                      if(cnt_ack_bit == 8'd47)
                          cs_n    <=  1'b1;
                      else
                          cs_n    <=  1'b0;
                  SEND_ACMD41:
                      if(cnt_cmd_bit == 8'd48)
                          cnt_cmd_bit     <=  8'd0;
                      else
                          begin
                              cs_n            <=  1'b0;
                              mosi            <=  ACMD41[8'd47 - cnt_cmd_bit];
                              init_end        <=  1'b0;
                              cnt_cmd_bit     <=  cnt_cmd_bit + 8'd1;
                          end
                  ACMD41_ACK:
                      if(cnt_ack_bit < 8'd47)
                          cs_n    <=  1'b0;
                      else
                          cs_n    <=  1'b1;
                  INIT_END:
                      begin
                          cs_n        <=  1'b1;
                          mosi        <=  1'b1;
                          init_end    <=  1'b1;
                      end
                  default:
                      begin
                          cs_n    <=  1'b1;
                          mosi    <=  1'b1;
                      end
              endcase
      
      //miso_dly:主输入从输出信号打一拍
      always@(posedge sys_clk_shift or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              miso_dly    <=  1'b0;
          else
              miso_dly    <=  miso;
      
      //ack_en:响应使能信号
      always@(posedge sys_clk_shift or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              ack_en  <=  1'b0;
          else    if(cnt_ack_bit == 8'd47)
              ack_en  <=  1'b0;
          else    if((miso == 1'b0) && (miso_dly == 1'b1) && (cnt_ack_bit == 8'd0))
              ack_en  <=  1'b1;
          else
              ack_en  <=  ack_en;
      
      //ack_data:响应数据
      //cnt_ack_bit:响应数据字节计数
      always@(posedge sys_clk_shift or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              begin
                  ack_data    <=  8'b0;
                  cnt_ack_bit <=  8'd0;
              end
          else    if(ack_en == 1'b1)
              begin
                  cnt_ack_bit     <=  cnt_ack_bit + 8'd1;
                  if(cnt_ack_bit < 8'd40)
                      ack_data    <=  {ack_data[38:0],miso_dly};
                  else
                      ack_data    <=  ack_data;
              end
          else
              cnt_ack_bit <=  8'd0;
      
      endmodule
      
      module  sd_read
      (
          input   wire            sys_clk         ,   //输入工作时钟,频率50MHz
          input   wire            sys_clk_shift   ,   //输入工作时钟,频率50MHz,相位偏移90度
          input   wire            sys_rst_n       ,   //输入复位信号,低电平有效
          input   wire            miso            ,   //主输入从输出信号
          input   wire            rd_en           ,   //数据读使能信号
          input   wire    [31:0]  rd_addr         ,   //读数据扇区地址
      
          output  wire            rd_busy         ,   //读操作忙信号
          output  reg             rd_data_en      ,   //读数据标志信号
          output  reg     [15:0]  rd_data         ,   //读数据
          output  reg             cs_n            ,   //片选信号
          output  reg             mosi                //主输出从输入信号
      );
      
      //********************************************************************//
      //****************** Parameter and Internal Signal *******************//
      //********************************************************************//
      //parameter define
      parameter   IDLE        =   3'b000  ,   //初始状态
                  SEND_CMD17  =   3'b001  ,   //读命令CMD17发送状态
                  CMD17_ACK   =   3'b011  ,   //CMD17响应状态
                  RD_DATA     =   3'b010  ,   //读数据状态
                  RD_END      =   3'b110  ;   //读结束状态
      parameter   DATA_NUM    =   12'd256 ;   //待读取数据字节数
      
      //wire  define
      wire    [47:0]  cmd_rd      ;   //数据读指令
      
      //reg   define
      reg     [2:0]   state       ;   //状态机状态
      reg     [7:0]   cnt_cmd_bit ;   //指令比特计数器
      reg             ack_en      ;   //响应使能信号
      reg     [7:0]   ack_data    ;   //响应数据
      reg     [7:0]   cnt_ack_bit ;   //响应数据字节计数
      reg     [11:0]  cnt_data_num;   //读出数据个数计数
      reg     [3:0]   cnt_data_bit;   //读数据比特计数器
      reg     [2:0]   cnt_end     ;   //结束状态时钟计数
      reg             miso_dly    ;   //主输入从输出信号打一拍
      reg     [15:0]  rd_data_reg ;   //读出数据寄存
      reg     [15:0]  byte_head   ;   //读数据字节头
      reg             byte_head_en;   //读数据字节头使能
      
      //********************************************************************//
      //***************************** Main Code ****************************//
      //********************************************************************//
      //rd_busy:读操作忙信号
      assign  rd_busy = (state != IDLE) ? 1'b1 : 1'b0;
      
      //cmd_rd:数据读指令
      assign  cmd_rd = {8'h51,rd_addr,8'hff};
      
      //miso_dly:主输入从输出信号打一拍
      always@(posedge sys_clk_shift or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              miso_dly    <=  1'b0;
          else
              miso_dly    <=  miso;
      
      //ack_en:响应使能信号
      always@(posedge sys_clk_shift or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              ack_en  <=  1'b0;
          else    if(cnt_ack_bit == 8'd15)
              ack_en  <=  1'b0;
          else    if((state == CMD17_ACK) && (miso == 1'b0)
                      && (miso_dly == 1'b1) && (cnt_ack_bit == 8'd0))
              ack_en  <=  1'b1;
          else
              ack_en  <=  ack_en;
      
      //ack_data:响应数据
      //cnt_ack_bit:响应数据字节计数
      always@(posedge sys_clk_shift or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              begin
                  ack_data    <=  8'b0;
                  cnt_ack_bit <=  8'd0;
              end
          else    if(ack_en == 1'b1)
              begin
                  cnt_ack_bit     <=  cnt_ack_bit + 8'd1;
                  if(cnt_ack_bit < 8'd8)
                      ack_data    <=  {ack_data[6:0],miso_dly};
                  else
                      ack_data    <=  ack_data;
              end
          else
              cnt_ack_bit <=  8'd0;
      
      //state:状态机状态跳转
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              state   <=  IDLE;
          else
              case(state)
                  IDLE:
                      if(rd_en == 1'b1)
                          state   <=  SEND_CMD17;
                      else
                          state   <=  state;
                  SEND_CMD17:
                      if(cnt_cmd_bit == 8'd47)
                          state   <=  CMD17_ACK;
                      else
                          state   <=  state;
                  CMD17_ACK:
                      if(cnt_ack_bit == 8'd15)
                          if(ack_data == 8'h00)
                              state   <=  RD_DATA;
                          else
                              state   <=  SEND_CMD17;
                      else
                          state   <=  state;
                  RD_DATA:
                      if((cnt_data_num == (DATA_NUM + 1'b1))
                          && (cnt_data_bit == 4'd15))
                          state   <=  RD_END;
                      else
                          state   <=  state;
                  RD_END:
                      if(cnt_end == 3'd7)
                          state   <=  IDLE;
                      else
                          state   <=  state;
                  default:state   <=  IDLE;
              endcase
      
      //cs_n:输出片选信号
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              cs_n    <=  1'b1;
          else    if(cnt_end == 3'd7)
              cs_n    <=  1'b1;
          else    if(rd_en == 1'b1)
              cs_n    <=  1'b0;
          else
              cs_n    <=  cs_n;
      
      //cnt_cmd_bit:指令比特计数器
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              cnt_cmd_bit     <=  8'd0;
          else    if(state == SEND_CMD17)
              cnt_cmd_bit     <=  cnt_cmd_bit + 8'd1;
          else
              cnt_cmd_bit     <=  8'd0;
      
      //mosi:主输出从输入信号
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              mosi    <=  1'b1;
          else    if(state == SEND_CMD17)
              mosi    <=  cmd_rd[8'd47 - cnt_cmd_bit];
          else
              mosi    <=  1'b1;
      
      //byte_head:读数据字节头
      always@(posedge sys_clk_shift or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              byte_head   <=  16'b0;
          else    if(byte_head_en == 1'b0)
              byte_head   <=  16'b0;
          else    if(byte_head_en == 1'b1)
              byte_head   <=  {byte_head[14:0],miso};
          else
              byte_head   <=  byte_head;
      
      //byte_head_en:读数据字节头使能
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              byte_head_en    <=  1'b0;
          else    if(byte_head == 16'hfffe)
              byte_head_en    <=  1'b0;
          else    if((state == RD_DATA) && (cnt_data_num == 12'd0)
                      && (cnt_data_bit == 4'd0))
              byte_head_en    <=  1'b1;
          else
              byte_head_en    <=  byte_head_en;
      
      //cnt_data_bit:读数据比特计数器
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              cnt_data_bit    <=  4'd0;
          else    if((state == RD_DATA) && (cnt_data_num >= 12'd1))
              cnt_data_bit    <=  cnt_data_bit + 4'd1;
          else
              cnt_data_bit    <=  4'd0;
      
      //cnt_data_num:读出数据个数计数
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              cnt_data_num    <=  12'd0;
          else    if(state == RD_DATA)
              if((cnt_data_bit == 4'd15) || (byte_head == 16'hfffe))
                  cnt_data_num    <=  cnt_data_num + 12'd1;
              else
                  cnt_data_num    <=  cnt_data_num;
          else
              cnt_data_num    <=  12'd0;
      
      //rd_data_reg:读出数据寄存
      always@(posedge sys_clk_shift or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              rd_data_reg <=  16'd0;
          else    if((state == RD_DATA) && (cnt_data_num >= 12'd1)
                      && (cnt_data_num <= DATA_NUM))
              rd_data_reg <=  {rd_data_reg[14:0],miso};
          else
              rd_data_reg <=  16'd0;
      
      //rd_data_en:读数据标志信号
      //rd_data:读数据
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              begin
                  rd_data_en  <=  1'b0;
                  rd_data     <=  16'd0;
              end
          else    if(state == RD_DATA)
              begin
                  if((cnt_data_bit == 4'd15) && (cnt_data_num <= DATA_NUM))
                      begin
                          rd_data_en  <=  1'b1;
                          rd_data     <=  rd_data_reg;
                      end
                  else
                      begin
                          rd_data_en  <=  1'b0;
                          rd_data     <=  rd_data;
                      end
              end
          else
              begin
                  rd_data_en  <=  1'b0;
                  rd_data     <=  16'd0;
              end
      
      //cnt_end:结束状态时钟计数
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              cnt_end <=  3'd0;
          else    if(state == RD_END)
              cnt_end <=  cnt_end + 3'd1;
          else
              cnt_end <=  3'd0;
      
      endmodule
      
      
      module  sd_write
      (
          input   wire            sys_clk         ,   //输入工作时钟,频率50MHz
          input   wire            sys_clk_shift   ,   //输入工作时钟,频率50MHz,相位偏移90度
          input   wire            sys_rst_n       ,   //输入复位信号,低电平有效
          input   wire            miso            ,   //主输入从输出信号
          input   wire            wr_en           ,   //数据写使能信号
          input   wire    [31:0]  wr_addr         ,   //写数据扇区地址
          input   wire    [15:0]  wr_data         ,   //写数据
      
          output  reg             cs_n            ,   //输出片选信号
          output  reg             mosi            ,   //主输出从输入信号
          output  wire            wr_busy         ,   //写操作忙信号
          output  wire            wr_req              //写数据请求信号
      );
      
      //********************************************************************//
      //****************** Parameter and Internal Signal *******************//
      //********************************************************************//
      //parameter define
      parameter   IDLE        =   3'b000  ,   //初始状态
                  SEND_CMD24  =   3'b001  ,   //写命令CMD24发送状态
                  CMD24_ACK   =   3'b011  ,   //CMD24响应状态
                  WR_DATA     =   3'b010  ,   //写数据状态
                  WR_BUSY     =   3'b110  ,   //SD卡写忙状态
                  WR_END      =   3'b111  ;   //写结束状态
      parameter   DATA_NUM    =   12'd256 ;   //待写入数据字节数
      parameter   BYTE_HEAD   =   16'hfffe;   //写数据字节头
      
      //wire  define
      wire    [47:0]  cmd_wr      ;   //数据写指令
      
      //reg   define
      reg     [2:0]   state       ;   //状态机状态
      reg     [7:0]   cnt_cmd_bit ;   //指令比特计数器
      reg             ack_en      ;   //响应使能信号
      reg     [7:0]   ack_data    ;   //响应数据
      reg     [7:0]   cnt_ack_bit ;   //响应数据字节计数
      reg     [11:0]  cnt_data_num;   //写入数据个数计数
      reg     [3:0]   cnt_data_bit;   //写数据比特计数器
      reg     [7:0]   busy_data   ;   //忙状态数据
      reg     [2:0]   cnt_end     ;   //结束状态时钟计数
      reg             miso_dly    ;   //主输入从输出信号打一拍
      
      //********************************************************************//
      //***************************** Main Code ****************************//
      //********************************************************************//
      //wr_busy:写操作忙信号
      assign  wr_busy = (state != IDLE) ? 1'b1 : 1'b0;
      
      //wr_req:写数据请求信号
      assign  wr_req = ((cnt_data_num <= DATA_NUM - 1'b1) && (cnt_data_bit == 4'd15))
                      ? 1'b1 : 1'b0;
      
      //cmd_wr:数据写指令
      assign  cmd_wr = {8'h58,wr_addr,8'hff};
      
      //miso_dly:主输入从输出信号打一拍
      always@(posedge sys_clk_shift or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              miso_dly    <=  1'b0;
          else
              miso_dly    <=  miso;
      
      //ack_en:响应使能信号
      always@(posedge sys_clk_shift or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              ack_en  <=  1'b0;
          else    if(cnt_ack_bit == 8'd15)
              ack_en  <=  1'b0;
          else    if((state == CMD24_ACK) && (miso == 1'b0)
                      && (miso_dly == 1'b1) && (cnt_ack_bit == 8'd0))
              ack_en  <=  1'b1;
          else
              ack_en  <=  ack_en;
      
      //ack_data:响应数据
      //cnt_ack_bit:响应数据字节计数
      always@(posedge sys_clk_shift or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              begin
                  ack_data    <=  8'b0;
                  cnt_ack_bit <=  8'd0;
              end
          else    if(ack_en == 1'b1)
              begin
                  cnt_ack_bit     <=  cnt_ack_bit + 8'd1;
                  if(cnt_ack_bit < 8'd8)
                      ack_data    <=  {ack_data[6:0],miso_dly};
                  else
                      ack_data    <=  ack_data;
              end
          else
              cnt_ack_bit <=  8'd0;
      
      //busy_data:忙状态数据
      always@(posedge sys_clk_shift or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              busy_data   <=  8'd0;
          else    if(state == WR_BUSY)
              busy_data   <=  {busy_data[6:0],miso};
          else
              busy_data   <=  8'd0;
      
      //state:状态机状态跳转
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              state   <=  IDLE;
          else
              case(state)
                  IDLE:
                      if(wr_en == 1'b1)
                          state   <=  SEND_CMD24;
                      else
                          state   <=  state;
                  SEND_CMD24:
                      if(cnt_cmd_bit == 8'd47)
                          state   <=  CMD24_ACK;
                      else
                          state   <=  state;
                  CMD24_ACK:
                      if(cnt_ack_bit == 8'd15)
                          if(ack_data == 8'h00)
                              state   <=  WR_DATA;
                          else
                              state   <=  SEND_CMD24;
                      else
                          state   <=  state;
                  WR_DATA:
                      if((cnt_data_num == (DATA_NUM + 1'b1))
                          && (cnt_data_bit == 4'd15))
                          state   <=  WR_BUSY;
                      else
                          state   <=  state;
                  WR_BUSY:
                      if(busy_data == 8'hff)
                          state   <=  WR_END;
                      else
                          state   <=  state;
                  WR_END:
                      if(cnt_end == 3'd7)
                          state   <=  IDLE;
                      else
                          state   <=  state;
                  default:state   <=  IDLE;
              endcase
      
      //cs_n:输出片选信号
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              cs_n    <=  1'b1;
          else    if(cnt_end == 3'd7)
              cs_n    <=  1'b1;
          else    if(wr_en == 1'b1)
              cs_n    <=  1'b0;
          else
              cs_n    <=  cs_n;
      
      //cnt_cmd_bit:指令比特计数器
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              cnt_cmd_bit     <=  8'd0;
          else    if(state == SEND_CMD24)
              cnt_cmd_bit     <=  cnt_cmd_bit + 8'd1;
          else
              cnt_cmd_bit     <=  8'd0;
      
      //mosi:主输出从输入信号
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              mosi    <=  1'b1;
          else    if(state == SEND_CMD24)
              mosi    <=  cmd_wr[8'd47 - cnt_cmd_bit];
          else    if(state == WR_DATA)
              if(cnt_data_num == 12'd0)
                  mosi    <=  BYTE_HEAD[15 - cnt_data_bit];
              else    if((cnt_data_num >= 12'd1) && (cnt_data_num <= DATA_NUM))
                  mosi    <=  wr_data[15 - cnt_data_bit];
              else
                  mosi    <=  1'b1;
          else
              mosi    <=  1'b1;
      
      //cnt_data_bit:写数据比特计数器
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              cnt_data_bit    <=  4'd0;
          else    if(state == WR_DATA)
              cnt_data_bit    <=  cnt_data_bit + 4'd1;
          else
              cnt_data_bit    <=  4'd0;
      
      //cnt_data_num:写入数据个数计数
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              cnt_data_num    <=  12'd0;
          else    if(state == WR_DATA)
              if(cnt_data_bit == 4'd15)
                  cnt_data_num    <=  cnt_data_num + 12'd1;
              else
                  cnt_data_num    <=  cnt_data_num;
          else
              cnt_data_num    <=  12'd0;
      
      //cnt_end:结束状态时钟计数
      always@(posedge sys_clk or negedge sys_rst_n)
          if(sys_rst_n == 1'b0)
              cnt_end <=  3'd0;
          else    if(state == WR_END)
              cnt_end <=  cnt_end + 3'd1;
          else
              cnt_end <=  3'd0;
      
      endmodule
      
      
    5. SDRAM控制器:我使用了官方的IP,但是依旧需要对IP的输入信号等进行处理,这就需要额外模块了,我试着按官方的参考设计写了一下,可耻扑街了
      这里贴一下官方例程网址:Gowin SDRAM Controller (gowinsemi.com.cn),包含了例程和文档,文档里面有引脚定义,和如何操作的详细解释(我也是写到这里才发现的,等会继续看看),我不明白的是用户信号里的sdrc_data_len数据长度的含义,很是迷惑,数据长度是指写入的数据量还是写入的数据bit位数还是10进制bit位数?以及I_sdrc_addr地址线的使用,我们存进一张680*480的图片数据,如何对正确找到地址?希望文档后面的内容能解答疑惑。
      还有一个初始化教程设置:2020-10-02_高云sdram-CSDN博客
      这里就不贴我写得一坨屑的代码了,有源再来。
    6. 关于高云的FIFO  IP的设置:如下设置了一个读写16位宽,256bit的fifo
       
    7. 后面还会更新,打算把SD卡模块和SDRAM分别验证完后再组合一起。

最新回复

 这里说的DDR3应该是控制器,不是存储器。   详情 回复 发表于 2024-2-19 17:45
点赞 关注
 
 

回复
举报

6593

帖子

0

TA的资源

五彩晶圆(高级)

沙发
 

感谢楼主分享在失败的尝试中出现的问题,有用

 
 
 

回复

9720

帖子

24

TA的资源

版主

板凳
 

 这里说的DDR3应该是控制器,不是存储器。

image.png (37.31 KB, 下载次数: 0)

image.png
个人签名虾扯蛋,蛋扯虾,虾扯蛋扯虾
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表