FPGA零基础学习精选|SDRSDRAM驱动设计

原创 FPGA技术江湖 2025-04-10 08:55

大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。


本系列将带来FPGA的系统性学习,从最基本的数字电路基础开始,最详细操作步骤,最直白的言语描述,手把手的“傻瓜式”讲解,让电子、信息、通信类专业学生、初入职场小白及打算进阶提升的职业开发者都可以有系统性学习的机会。

系统性的掌握技术开发以及相关要求,对个人就业以及职业发展都有着潜在的帮助,希望对大家有所帮助。后续会陆续更新 Xilinx 的 Vivado、ISE 及相关操作软件的开发的相关内容,学习FPGA设计方法及设计思想的同时,实操结合各类操作软件,会让你在技术学习道路上无比的顺畅,告别技术学习小BUG卡破脑壳,告别目前忽悠性的培训诱导,真正的去学习去实战应用,这种快乐试试你就会懂的。话不多说,上货。



高级设计:SDR SDRAM 驱动设计


作者:郝旭帅  校对:陆辉


本篇实现基于叁芯智能科技的SANXIN -B01 FPGA开发板,以下为配套的教程,如有入手开发板,可以登录官方淘宝店购买,还有配套的学习视频。

FPGA入门进阶板卡推荐 | SANXIN B02 FPGA开发板(Xilinx)

FPGA入门进阶板卡推荐 | SANXIN B01 FPGA开发板(Intel)

SANXIN-B01开发板verilog教程V3电子版


FPGA工程师线上研修班,常年开班,随时报名,新增高速接口、光纤通信等内容!



本系列的技术文档、源工程代码可以登录叁芯智能科技官方技术论坛下载。

论坛网址:www.sxznfpga.com


随机访问存储器(RAM)分为静态RAM(SRAM)和动态RAM(DRAM)。由于动态存储器存储单元的结构非常简单,所以它能达到的集成度远高于静态存储器。但是动态存储器的存取速度不如静态存储器快。


RAM的动态存储单元是利用电容可以存储电荷的原理制成的。由于存储单元的机构能够做得很简单,所以在大容量、高集成度的RAM中得到了普遍的应用。但是由于电容的容量很小,而漏电流又不可能绝对等于零,所以电荷保存的时间有限。为了及时补充漏掉的电荷以避免存储的信号丢失,必须定时地给电容补充电荷,通常将这种操作称为刷新。


行列地址线被选中后,数据线(data_bit)直接和电容相连接。当写入时,数据线给电容充放电;读取时,电容将数据线拉高或者置低。


SDRAM 的全称即同步动态随机存储器(Synchronous Dynamic Random Access Memory);这里的同步是指其时钟频率与对应控制器的系统时钟频率相同,并且内部命令的发送与数据传输都是以该时钟为基准;动态是指存储阵列需要不断的刷新来保证数据不丢失。


SDR SDRAM中的SDR是指单数据速率,即每一根数据线上,每个时钟只传输一个bit的数据。SDR SDRAM的时钟频率可以达到100MHz以上,按照100MHz的速率计算,一片16位数据宽度的SDR SDRAM的读写数据带宽可以达到1.6Gbit/s。


SANXIN – B01的开发板上有一个容量为256Mbit(16M x 16bit)的SDR SDRAM(H57V2562GTR)。其内部存储时,分为了4个独立的区域(BANK),每个bank为4Mx16bit的存储空间;每个bank在存储时,按照二维的方式进行存储,利用行列来进行确定,有8192行(13bit地址线),有512列(9bit地址线),8192 x 512为4M的存储量。


在进行指定某个地址时,共需要2位bank地址,13位行地址,9位列地址,合计共24位地址。但是在SDR SDRAM的指定某个地址时,行地址和列地址不是同时给出,SDR SDRAM采用行列地址线复用,所以地址线合计为2(bank 地址)+13(行、列地址复用)。


SDR SDRAM需要时钟端和时钟使能端。SDR SDRAM所有的操作都依靠于此时钟;当时钟使能端无效时,SDR SDRAM自动忽略时钟上升沿。


SDR SDRAM拥有四个命令控制线,分别为CS、RAS、CAS、WE。组成的命令表如下:


在写入数据时,有时会出现不想对某8bit进行写入,就可以采用DQM进行控制。


SDR SDRAM的内部机构为:


由于SDR SDRAM为DRAM,内部的存储都是靠电容进行保存数据,电容的保持数据的时间为64ms,SDR SDRAM每次只能够刷新一行,为了不丢失任何数据,所以要保证64ms内,将所有的行都要刷新一遍。


SDR SDRAM支持读写的长度为1、2、4、8和一行(整页)。


具体的SDR SDRAM的介绍可以查看手册。下面只介绍几个相对重要的时序图。


在SDR SDRAM正常使用之前,需要进行初始化。初始化的时序图如下:

在PRECHARGE时,A10为高,表示选中所有的bank;A10为低,表示选中BA0、BA1所指定的bank。初始化中,A10置高。


在LOAD MOOE REGISTER中,采用地址线进行配置模式寄存器。说明如下:


在模式配置中,利用CL(CAS Latency)表示列选通潜伏期,利用BL(Burst Length)表示突发长度。


SDR SDRAM中有内部的刷新控制器和刷新的行计数器,外部控制器只需要保证在64ms之内进行8192次刷新即可。


在进行PRECHARGE时,A10要为高电平。


SDR SDRAM中,我们可以在任意位置进行写入。写入的时序图如下:


SDR SDRAM中,我们可以在任意位置进行读出。读出的时序图如下:


在各个时序中的时序参数如下:



  • 设计要求

设计一个突发长度为2,列选通潜伏期为2的SDR SDRAM的控制器。


  • 设计分析

该控制器共有四部分功能,初始化、刷新、写和读。四部分的执行控制采用一个模块来控制。


SDR SDRAM必须要进行初始化,初始化只用执行一次。然后启动一个计时器,等计时器达到后,进行刷新。在刷新的间隔中,根据读写的要求进行读写。


四个模块都会对SDR SDRAM的命令线和地址线进行控制,所以输出时,采用多路选择器对齐进行选择输出。


四个模块按照对应的时序图进行编写代码即可。


  • 架构设计和信号说明

该控制器命名为sdr_drive。


pll_sdr(锁相环模块):产生驱动所需要的100MHz的时钟(0度相位)、SDR SDRAM所需要的100MHz的时钟(270度相位)、以及PLL锁定信号当作系统复位使用。


timer(刷新计时器):当启动计时器后,开始计时,当计时到规定时间后,输出刷新请求,计数器直接清零计数计数。当控制器响应后,输出清除信号后,刷新请求拉低。


refresh(刷新模块)、init(初始化模块)、sdr_write(写模块)、sdr_read(读模块):当启动模块后,按照规定的时序进行输出即可,然后输出完成信号。


sdr_ctrl(控制模块):控制各个模块协调工作。


mux4_1(四选一多路选择器模块):选择对应的bus总线作为输出。


*_bus的组成为:高四位为sdr_cs_n、sdr_ras_n、sdr_cas_n、sdr_we_n。然后是bank的两位,后续为13位的sdr_addr。


  • sdr_drive_head声明

将驱动中用到各种参数定义在该文件中。

`define       SDR_ADDR_WIDTH                    13`define       SDR_COL_ADDR_WIDTH                9`define       SDR_REFRESH_TIME                  64_000_000
`define       ADDR_WIDTH   2 + `SDR_ADDR_WIDTH + `SDR_COL_ADDR_WIDTH`define       BUS_WIDTH    4 + 2 + `SDR_ADDR_WIDTH
`define       CMD_INH                           4'b1000`define       NOP                               4'b0111`define       ACT                               4'b0011`define       RD                                4'b0101`define       WR                                4'b0100`define       BT                                4'b0110`define       PREC                              4'b0010`define       REFR                              4'b0001`define       LMR                               4'b0000
`define       PU_DELAY                          20_000`define       Trp                               3`define       Trfc                              7`define       Tmrd                              3`define       Trcd                              3`define       Twr                               3`define       Tcl                               2
`define       CODE                             13'b000_0_00_010_0_001`define     REFRESH_TIME  (`SDR_REFRESH_TIME/(2**`SDR_ADDR_WIDTH))/10


  • pll_sdr设计实现

该模块为IP core,输出0相位的100MHz(系统时钟)和270相位的100MHz(SDR的时钟)。系统设计中,信号在上升沿输出;对于外部器件(相位调整为270),能够较好的满足建立和保持时间。


  • init设计实现

该模块负责将SDR SDRAM进行初始化。上电延迟(PU_DELAY)设置为200us;预充电时间(Trp)设置为3个时钟周期(30ns);自刷新时间(Trfc)设置为7个时钟周期(70ns);模式寄存器应用时间(Tmrd)设置为3个时钟周期(30ns);突发长度为2;列选通潜伏期为3。


按照对应的初始化的时序图,做出如下设计。


本模块采用状态机的方式设计实现。


设计代码为:

`include "../rtl/sdr_drive_head.v"
module init (
  input     wire                                  clk,  input     wire                                  rst_n,
  input     wire                                  init_en,  output    reg                                   init_done,
  output    wire        [`BUS_WIDTH - 1 : 0]      init_bus);
  localparam      IDLE              =             7'b000_0001;  localparam      PUD               =             7'b000_0010;  localparam      PRECHARGE         =             7'b000_0100;  localparam      AUTOREFR1         =             7'b000_1000;  localparam      AUTOREFR2         =             7'b001_0000;  localparam      LMR_STATE         =             7'b010_0000;  localparam      INITDONE          =             7'b100_0000;
  reg                   [6:0]                     c_state;  reg                   [6:0]                     n_state;  wire                  [1:0]                     sdr_bank;  reg                   [3:0]                     sdr_cmd;  reg                   [`SDR_ADDR_WIDTH - 1 : 0] sdr_addr;  reg                   [14:0]                    cnt;                    
  assign sdr_bank = 2'b00;  assign init_bus = {sdr_cmd,sdr_bank,sdr_addr};
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end
  always @ * begin    case (c_state)      IDLE        :     begin        if (init_en == 1'b1)          n_state = PUD;        else          n_state = IDLE;      end
      PUD         :     begin        if (cnt == `PU_DELAY - 1'b1)          n_state = PRECHARGE;        else          n_state = PUD;      end
      PRECHARGE   :     begin        if (cnt == `Trp - 1'b1)          n_state = AUTOREFR1;        else          n_state = PRECHARGE;      end
      AUTOREFR1   :     begin        if (cnt == `Trfc - 1'b1)          n_state = AUTOREFR2;        else            n_state = AUTOREFR1;      end
      AUTOREFR2   :     begin        if (cnt == `Trfc - 1'b1)          n_state = LMR_STATE;        else            n_state = AUTOREFR2;      end
      LMR_STATE   :     begin        if (cnt == `Tmrd - 1'b1)          n_state = INITDONE;        else          n_state = LMR_STATE;      end
      INITDONE    :     begin        n_state = INITDONE;      end
      default     :   n_state = IDLE;    endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_cmd <= `NOP;    else      case (c_state)        IDLE        :   sdr_cmd <= `NOP;        PUD         :   begin          if (cnt == `PU_DELAY - 1'b1)            sdr_cmd <= `PREC;          else            sdr_cmd <= `NOP;        end        PRECHARGE   :   begin          if (cnt == `Trp - 1'b1)            sdr_cmd <= `REFR;          else            sdr_cmd <= `NOP;        end        AUTOREFR1   :   begin          if (cnt == `Trfc - 1'b1)            sdr_cmd <= `REFR;          else            sdr_cmd <= `NOP;        end        AUTOREFR2   :   begin          if (cnt == `Trfc - 1'b1)            sdr_cmd <= `LMR;          else            sdr_cmd <= `NOP;        end        LMR_STATE   :   sdr_cmd <= `NOP;        INITDONE    :   sdr_cmd <= `NOP;        default     :   sdr_cmd <= `NOP;      endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      cnt <= 15'd0;    else      case (c_state)        IDLE        :   cnt <= 16'd0;        PUD         :   begin          if (cnt < `PU_DELAY - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 16'd0;        end
        PRECHARGE    :   begin          if (cnt < `Trp - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 16'd0;        end        AUTOREFR1    :   begin          if (cnt < `Trfc - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 16'd0;        end        AUTOREFR2    :   begin          if (cnt < `Trfc - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 16'd0;        end        LMR_STATE    :   begin          if (cnt < `Tmrd - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 16'd0;        end        INITDONE    :   cnt <= 16'd0;        default     :   cnt <= 16'd0;      endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      init_done <= 1'b0;    else      if (c_state == LMR_STATE && cnt == `Tmrd - 1'b1)        init_done <= 1'b1;      else        init_done <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_addr <= 0;    else      if (c_state == PUD && cnt == `PU_DELAY - 1'b1)        sdr_addr[10] <= 1'b1;      else        if (c_state == AUTOREFR2 && cnt == `Trfc - 1'b1)          sdr_addr <= `CODE;        else          sdr_addr <= 0;  end
endmodule


  • timer设计实现


SDR SDRAM内部构造为DRAM,需要不间断的刷新,要求64ms刷新一遍。每次刷新为一行,开发板上的SDR SDRAM共有8192行,平均需要7812.5ns刷新一次,我们选择7810刷新一次。


到达规定的刷新时间时,控制器有可能正在进行其他的操作。在设计时,达到时间后,发出刷新请求,当外部执行刷新后,将次请求清除。发出刷新请求的同时,计数器重新归零计数。

`include "../rtl/sdr_drive_head.v"
module timer (
  input   wire                    clk,  input   wire                    rst_n,
  input   wire                    time_en,  input   wire                    req_clr,
  output  reg                     refresh_req);
  reg               [9:0]         cnt;
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      cnt <= 10'd0;    else      if (time_en == 1'b1 && cnt < `REFRESH_TIME - 1'b1)        cnt <= cnt + 1'b1;      else          cnt <= 10'd0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      refresh_req <= 1'b0;    else      if (cnt == `REFRESH_TIME - 1'b1)        refresh_req <= 1'b1;      else        if (req_clr == 1'b1)          refresh_req <= 1'b0;        else          refresh_req <= refresh_req;  end
endmodule


  • refresh设计实现

该模块负责刷新,按照对应的时序图进行控制即可。


该模块利用状态机的方式实现。状态转移图如下:


设计代码为:

`include "../rtl/sdr_drive_head.v"
module refresh (
  input     wire                                  clk,  input     wire                                  rst_n,
  input     wire                                  refresh_en,  output    reg                                   refresh_done,
  output    wire        [`BUS_WIDTH - 1 : 0]      refresh_bus);
  localparam      IDLE              =             5'b0_0001;  localparam      PRECHARGE         =             5'b0_0010;  localparam      AUTOREFR1         =             5'b0_0100;  localparam      AUTOREFR2         =             5'b0_1000;  localparam      REFRDONE          =             5'b1_0000;
  reg                   [4:0]                     c_state;  reg                   [4:0]                     n_state;  wire                  [1:0]                     sdr_bank;  reg                   [3:0]                     sdr_cmd;  reg                   [`SDR_ADDR_WIDTH - 1 : 0] sdr_addr;  reg                   [3:0]                     cnt;                    
  assign sdr_bank = 2'b00;  assign refresh_bus = {sdr_cmd,sdr_bank,sdr_addr};
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end
  always @ * begin    case (c_state)      IDLE        :     begin        if (refresh_en == 1'b1)          n_state = PRECHARGE;        else          n_state = IDLE;      end
      PRECHARGE   :     begin        if (cnt == `Trp - 1'b1)          n_state = AUTOREFR1;        else          n_state = PRECHARGE;      end
      AUTOREFR1   :     begin        if (cnt == `Trfc - 1'b1)          n_state = AUTOREFR2;        else            n_state = AUTOREFR1;      end
      AUTOREFR2   :     begin        if (cnt == `Trfc - 1'b1)          n_state = REFRDONE;        else            n_state = AUTOREFR2;      end
      REFRDONE    :     begin        n_state = IDLE;      end
      default     :   n_state = IDLE;    endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_cmd <= `NOP;    else      case (c_state)        IDLE        :   begin          if (refresh_en == 1'b1)            sdr_cmd <= `PREC;          else            sdr_cmd <= `NOP;        end        PRECHARGE   :   begin          if (cnt == `Trp - 1'b1)            sdr_cmd <= `REFR;          else            sdr_cmd <= `NOP;        end        AUTOREFR1   :   begin          if (cnt == `Trfc - 1'b1)            sdr_cmd <= `REFR;          else            sdr_cmd <= `NOP;        end        AUTOREFR2   :   sdr_cmd <= `NOP;
        REFRDONE    :   sdr_cmd <= `NOP;        default     :   sdr_cmd <= `NOP;      endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      cnt <= 4'd0;    else      case (c_state)        IDLE        :   cnt <= 4'd0;
        PRECHARGE    :   begin          if (cnt < `Trp - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 4'd0;        end        AUTOREFR1    :   begin          if (cnt < `Trfc - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 4'd0;        end        AUTOREFR2    :   begin          if (cnt < `Trfc - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 4'd0;        end        REFRDONE    :   cnt <= 4'd0;        default     :   cnt <= 4'd0;      endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      refresh_done <= 1'b0;    else      if (c_state == AUTOREFR2 && cnt == `Trfc - 1'b1)        refresh_done <= 1'b1;      else        refresh_done <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_addr <= 0;    else      if (c_state == IDLE && refresh_en == 1'b1)        sdr_addr[10] <= 1'b1;      else        sdr_addr <= 0;  end
endmodule


  • sdr_write设计实现

该模块负责将外部的数据写入到规定的地址中去。在SDR SDRAM中,每操作(读写)一次,都会引起该存储位的漏电,每次结束时,可以进行预充电。SDR SDRAM提供了自动预充电的机制,在读写命令时,sdr_addr[10]=1,即可自动预充电。在设计时,应该要为自动预充电预留出足够的时间。


根据对应的写入时序图,利用状态机完成此设计。


设计代码如下:

`include "../rtl/sdr_drive_head.v"
module sdr_write (
  input     wire                            clk,  input     wire                            rst_n,
  input     wire                            write_en,  input     wire  [`ADDR_WIDTH - 1 : 0]     wr_addr,  input     wire  [31:0]                    wr_data,
  output    reg   [15:0]                    odq,  output    wire  [`BUS_WIDTH - 1 : 0]      wr_bus,  output    reg                             wr_done);
  localparam    IDLE            =           4'b0001;  localparam    ACT_STATE       =           4'b0010;  localparam    WR1             =           4'b0100;  localparam    WR2             =           4'b1000;
  reg                   [3:0]                     c_state;  reg                   [3:0]                     n_state;  wire                  [1:0]                     sdr_bank;  reg                   [3:0]                     sdr_cmd;  reg                   [`SDR_ADDR_WIDTH - 1 : 0] sdr_addr;  reg                   [14:0]                    cnt;                    
  assign sdr_bank = wr_addr[23:22];  assign wr_bus = {sdr_cmd,sdr_bank,sdr_addr};
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end
  always @ * begin    case (c_state)      IDLE        :   begin        if (write_en == 1'b1)          n_state = ACT_STATE;        else          n_state = IDLE;      end
      ACT_STATE   :   begin        if (cnt == `Trcd - 1'b1)          n_state = WR1;        else          n_state = ACT_STATE;      end
      WR1         :    n_state = WR2;
      WR2         :   begin        if (cnt == `Twr + `Trp - 1'b1)          n_state = IDLE;        else          n_state = WR2;      end
      default     :   n_state = IDLE;    endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_cmd <= `NOP;    else      if (c_state == IDLE && write_en == 1'b1)        sdr_cmd <= `ACT;      else        if (c_state == ACT_STATE && cnt == `Trcd - 1'b1)          sdr_cmd <= `WR;        else          sdr_cmd <= `NOP;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_addr <= 0;    else      if (c_state == IDLE && write_en == 1'b1)        sdr_addr <= wr_addr[21:9];      else        if (c_state == ACT_STATE && cnt == `Trcd - 1'b1) begin          sdr_addr[10] <= 1'b1;          sdr_addr[8:0] <= wr_addr[8:0];        end        else          sdr_addr <= 0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      cnt <= 4'd0;    else      if (c_state == ACT_STATE && cnt < `Trcd - 1'b1)        cnt <= cnt + 1'b1;      else        if (c_state == WR2 && cnt < `Twr + `Trp - 1'b1)          cnt <= cnt + 1'b1;        else          cnt <= 4'd0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      odq <= 16'd0;    else      if (c_state == ACT_STATE && cnt == `Trcd - 1'b1)        odq <= wr_data[15:0];      else        if (c_state == WR1)          odq <= wr_data[31:16];        else          odq <= 16'd0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wr_done <= 1'b0;    else      if (c_state == WR2 && cnt < `Twr + `Trp - 1'b1)        wr_done <= 1'b1;      else        wr_done <= 1'b0;  end
endmodule


  • sdr_read设计实现

该模块负责从指定的地址中,将数据读出。


按照对应的读时序图即可实现功能,本模块采用状态机方式实现,状态转移图如下:


设计代码为:

module sdr_read (
  input   wire                              clk,  input   wire                              rst_n,
  input   wire                              read_en,  input   wire    [`ADDR_WIDTH - 1 : 0]     rd_addr,
  input   wire    [15:0]                    sdr_dq,
  output  reg     [31:0]                    rd_data,  output  reg                               rd_done,
  output  wire    [`BUS_WIDTH - 1 : 0]      rd_bus);
  localparam    IDLE            =           5'b00001;  localparam    ACT_STATE       =           5'b00010;  localparam    READ_STATE      =           5'b00100;  localparam    RD1             =           5'b01000;  localparam    RD2             =           5'b10000;
  reg                   [4:0]                     c_state;  reg                   [4:0]                     n_state;  wire                  [1:0]                     sdr_bank;  reg                   [3:0]                     sdr_cmd;  reg                   [`SDR_ADDR_WIDTH - 1 : 0] sdr_addr;  reg                   [3:0]                     cnt;                    
  assign sdr_bank = rd_addr[23:22];  assign rd_bus = {sdr_cmd,sdr_bank,sdr_addr};
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end
  always @ * begin    case (c_state)      IDLE        :   begin        if (read_en == 1'b1)          n_state = ACT_STATE;        else          n_state = IDLE;      end
      ACT_STATE   :   begin        if (cnt == `Trcd - 1'b1)          n_state = READ_STATE;        else          n_state = ACT_STATE;      end
      READ_STATE  :   begin        if (cnt == `Tcl)          n_state = RD1;        else          n_state = READ_STATE;      end
      RD1         :   n_state = RD2;
      RD2         :   begin        if (cnt == `Trp - 1'b1)          n_state = IDLE;        else          n_state = RD2;      end
      default     :   n_state = IDLE;    endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_cmd <= `NOP;    else      if (c_state == IDLE && read_en == 1'b1)        sdr_cmd <= `ACT;      else        if (c_state == ACT_STATE && cnt == `Trcd - 1'b1)          sdr_cmd <= `RD;        else          sdr_cmd <= `NOP;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_addr <= 0;    else      if (c_state == IDLE && read_en == 1'b1)        sdr_addr <= rd_addr[21:9];      else        if (c_state == ACT_STATE && cnt == `Trcd - 1'b1) begin          sdr_addr[10] <= 1'b1;          sdr_addr[8:0<= rd_addr[8:0];        end        else          sdr_addr <= 0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      cnt <= 4'd0;    else      case (c_state)        IDLE      :     cnt <= 4'd0;        ACT_STATE :     begin          if (cnt < `Trcd - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 4'd0;        end        READ_STATE:     begin          if (cnt < `Tcl)            cnt <= cnt + 1'b1;          else            cnt <= 4'd0;        end
        RD1       :     cnt <= 4'd0;        RD2       :     begin          if (cnt < `Trp - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 4'd0;        end        default   :     cnt <= 4'd0;      endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rd_data <= 32'd0;    else      if (c_state == READ_STATE && cnt == `Tcl)        rd_data[15:0] <= sdr_dq;      else        if (c_state == RD1)          rd_data[31:16] <= sdr_dq;        else          rd_data <= rd_data;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rd_done <= 1'b0;    else      if (c_state == RD2 && cnt < `Trp - 1'b1)        rd_done <= 1'b1;      else        rd_done <= 1'b0;  end
endmodule


  • mux4_1设计实现

该模块负责选择出对应的bus,然后将对应位作为输出即可。


设计代码为:

module mux4_1 (
  input   wire  [`BUS_WIDTH - 1 : 0]      init_bus,  input   wire  [`BUS_WIDTH - 1 : 0]      refresh_bus,  input   wire  [`BUS_WIDTH - 1 : 0]      wr_bus,  input   wire  [`BUS_WIDTH - 1 : 0]      rd_bus,  input   wire  [1:0]                     mux_sel,
  output  wire  [1 : 0]                   sdr_bank,  output  wire  [`ADDR_WIDTH - 1 : 0]     sdr_addr,  output  wire                            sdr_cs_n,  output  wire                            sdr_ras_n,  output  wire                            sdr_cas_n,  output  wire                            sdr_we_n);
  reg           [`BUS_WIDTH - 1 : 0]      sdr_bus;
  assign sdr_cs_n = sdr_bus[18];  assign sdr_ras_n = sdr_bus[17];  assign sdr_cas_n = sdr_bus[16];  assign sdr_we_n = sdr_bus[15];
  assign sdr_bank = sdr_bus[14:13];
  assign sdr_addr = sdr_bus[12:0];
  always @ * begin    case (mux_sel)      2'b00       :   sdr_bus = init_bus;      2'b01       :   sdr_bus = refresh_bus;      2'b10       :   sdr_bus = wr_bus;      2'b11       :   sdr_bus = rd_bus;      default     :   sdr_bus = init_bus;    endcase  end
endmodule


  • sdr_ctrl设计实现

该模块负责调度整个控制器,利用状态机实现。


设计代码为:

`include "../rtl/sdr_drive_head.v"
module sdr_ctrl (
  input   wire                          clk,  input   wire                          rst_n,
  input   wire                          wr_en,  input   wire                          rd_en,  input   wire    [`ADDR_WIDTH - 1 : 0] addr,  input   wire    [31:0]                wdata,  output  reg     [31:0]                rdata,  output  reg                           rd_valid,
  output  wire                          sdr_busy,
  output  reg     [1:0]                 mux_sel,
  output  reg                           init_en,  input   wire                          init_done,
  output  reg                           time_en,  input   wire                          refresh_req,  output  reg                           req_clr,
  output  reg                           refresh_en,  input   wire                          refresh_done,
  output  reg                           out_en,  output  reg                           write_en,  output  reg     [`ADDR_WIDTH - 1 : 0] wr_addr,  output  reg     [31:0]                wr_data,  input   wire                          wr_done,
  output  reg                           read_en,  output  reg     [`ADDR_WIDTH - 1 : 0] rd_addr,  input   wire    [31:0]                rd_data,  input   wire                          rd_done);
  localparam      IDLE            =     6'b000_001;  localparam      INIT_STATE      =     6'b000_010;  localparam      REFRESH_STATE   =     6'b000_100;  localparam      NO_BUSY         =     6'b001_000;  localparam      WR_STATE        =     6'b010_000;  localparam      RD_STATE        =     6'b100_000;
  reg             [5:0]                 c_state;  reg             [5:0]                 n_state;  reg                                   wren;  reg                                   wren_clr;  reg                                   rden;  reg                                   rden_clr;  reg             [`ADDR_WIDTH - 1 : 0] addrr;  reg             [31:0]                wdatar;  reg                                   busy;
  assign sdr_busy = busy | rd_en | wr_en;
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wren <= 1'b0;    else      if (wr_en == 1'b1)        wren <= 1'b1;      else        if (wren_clr == 1'b1)          wren <= 1'b0;        else          wren <= wren;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rden <= 1'b0;    else      if (rd_en == 1'b1)        rden <= 1'b1;      else        if (rden_clr == 1'b1)          rden <= 1'b0;        else          rden <= rden;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wdatar <= 32'd0;    else      if (wr_en == 1'b1)        wdatar <= wdata;      else        wdatar <= wdatar;  end 
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      addrr <= 0;    else      if (wr_en == 1'b1 || rd_en == 1'b1)        addrr <= addr;      else        addrr <= addrr;  end 
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end
  always @ * begin    case (c_state)      IDLE              :   n_state = INIT_STATE;
      INIT_STATE        :   begin        if (init_done == 1'b1)          n_state = REFRESH_STATE;        else          n_state = INIT_STATE;      end
      REFRESH_STATE     :   begin        if (refresh_done == 1'b1)          n_state = NO_BUSY;        else          n_state = REFRESH_STATE;      end
      NO_BUSY           :   begin        if (refresh_req == 1'b1)          n_state = REFRESH_STATE;        else          if (wren == 1'b1)            n_state = WR_STATE;          else            if (rden == 1'b1)              n_state = RD_STATE;            else              n_state = NO_BUSY;      end
      WR_STATE          :   begin        if (wr_done == 1'b1)          n_state = NO_BUSY;        else          n_state = WR_STATE;      end
      RD_STATE          :   begin        if (rd_done == 1'b1)          n_state = NO_BUSY;        else          n_state = RD_STATE;      end
      default           :   n_state = IDLE;    endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      busy <= 1'b1;    else      if (c_state == NO_BUSY && wren == 1'b0 && rden == 1'b0 && refresh_req == 1'b0)        busy <= rd_en | wr_en;      else        busy <= 1'b1;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      init_en <= 1'b0;    else      if (c_state == IDLE)        init_en <= 1'b1;      else        init_en <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      time_en <= 1'b0;    else      if (c_state == INIT_STATE && init_done == 1'b1)        time_en <= 1'b1;      else        time_en <= time_en;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      refresh_en <= 1'b0;    else      if (c_state == INIT_STATE && init_done == 1'b1)        refresh_en <= 1'b1;      else        if (c_state == NO_BUSY && refresh_req == 1'b1)          refresh_en <= 1'b1;        else          refresh_en <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      req_clr <= 1'b0;    else      if (c_state == NO_BUSY && refresh_req == 1'b1)        req_clr <= 1'b1;      else        req_clr <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      write_en <= 1'b0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b1)        write_en <= 1'b1;     else        write_en <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      out_en <= 1'b0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b1)        out_en <= 1'b1;     else        if (c_state == WR_STATE && wr_done == 1'b1)          out_en <= 1'b0;        else          out_en <= out_en;  end 
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wr_addr <= 0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b1)        wr_addr <= addrr;     else        wr_addr <= wr_addr;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wr_data <= 0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b1)        wr_data <= wdatar;     else        wr_data <= wr_data;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wren_clr <= 1'b0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b1)        wren_clr <= 1'b1;     else        wren_clr <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rden_clr <= 1'b0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b0 && rden == 1'b1)        rden_clr <= 1'b1;     else        rden_clr <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      read_en <= 1'b0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b0 && rden == 1'b1)        read_en <= 1'b1;     else        read_en <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rd_addr <= 0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b0 && rden == 1'b1)        rd_addr <= addrr;     else        rd_addr <= rd_addr;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rdata <= 32'd0;    else      if (c_state == RD_STATE && rd_done == 1'b1)        rdata <= rd_data;      else        rdata <= rdata;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rd_valid <= 1'b0;    else      if (c_state == RD_STATE && rd_done == 1'b1)        rd_valid <= 1'b1;      else        rd_valid <= 1'b0;  end 
  always @ (posedge clk, negedge rst_n)  begin    if (rst_n == 1'b0)      mux_sel <= 2'b00;    else      case (c_state)        IDLE        :       mux_sel <= 2'b00;        INIT_STATE  :       begin          if (init_done == 1'b1)            mux_sel <= 2'b01;          else              mux_sel <= mux_sel;        end        REFRESH_STATE:      mux_sel <= mux_sel;        NO_BUSY     :       begin          if (refresh_req == 1'b1)            mux_sel <= 2'b01;          else            if (wren == 1'b1)              mux_sel <= 2'b10;            else              if (rden == 1'b1)                mux_sel <= 2'b11;              else                mux_sel <= mux_sel;        end        RD_STATE    :   mux_sel <= mux_sel;        WR_STATE    :   mux_sel <= mux_sel;
        default     :   mux_sel <= 2'b00;      endcase  end
endmodule


为了防止在进行刷新的起始部分丢失读写命令,所以在设计时,加入了缓存结构,只要有读写命令时,都会进行保存。在读写执行时,才会清除此命令。


  • RTL仿真

为了能够仿真此设计,需要用到SDR SDRAM的仿真模型。仿真模型在msim的sdr_sim_module中,将其修改为行线为13bit,列为9bit,每个bank有4194304个存储空间。


在仿真时,在第二个bank,第五行,第10列,写入一个随机值。然后读取出来。


仿真代码为:

`timescale 1ns/1ps
module sdr_drive_tb;
  reg                     clk;  reg                     rst_n;
  wire                    sys_clk;  wire                    sys_rst_n;
  wire                    sdr_busy;  reg                     wr_en;  reg                     rd_en;  reg       [23:0]        addr;  reg       [31:0]        wdata;  wire      [31:0]        rdata;  wire                    rd_valid;
  wire                    sdr_clk;  wire                    sdr_cke;  wire                    sdr_cs_n;  wire                    sdr_ras_n;  wire                    sdr_cas_n;  wire                    sdr_we_n;  wire      [15:0]        sdr_dq;  wire      [1:0]         sdr_bank;  wire      [1:0]         sdr_dqm;  wire      [12:0]        sdr_addr;
  sdr_drive sdr_drive_inst(
      .clk                  (clk),      .rst_n                (rst_n),
      .sys_clk              (sys_clk),      .sys_rst_n            (sys_rst_n),
    //  local      .sdr_busy             (sdr_busy),      .wr_en                (wr_en),      .rd_en                (rd_en),      .addr                 (addr),      .wdata                (wdata),      .rdata                (rdata),      .rd_valid             (rd_valid),
    //  sdr      .sdr_clk              (sdr_clk),      .sdr_cke              (sdr_cke),      .sdr_cs_n             (sdr_cs_n),      .sdr_ras_n            (sdr_ras_n),      .sdr_cas_n            (sdr_cas_n),      .sdr_we_n             (sdr_we_n),      .sdr_bank             (sdr_bank),      .sdr_addr             (sdr_addr),      .sdr_dqm              (sdr_dqm),      .sdr_dq               (sdr_dq)    );
  mt48lc32m16a2 mt48lc32m16a2_inst(      .Dq                   (sdr_dq),       .Addr                 (sdr_addr),       .Ba                   (sdr_bank),       .Clk                  (sdr_clk),       .Cke                  (sdr_cke),       .Cs_n                 (sdr_cs_n),       .Ras_n                (sdr_ras_n),       .Cas_n                (sdr_cas_n),       .We_n                 (sdr_we_n),       .Dqm                  (sdr_dqm)    );
  initial clk = 1'b0;  always # 10 clk = ~clk;
  initial begin    rst_n = 1'b0;    wr_en = 1'b0;    rd_en = 1'b0;    addr = {2'b01, 13'd5,9'd10};    wdata = 32'd0;    # 201    rst_n = 1'b1;    @ (negedge sdr_busy);    @ (posedge sys_clk);    # 2;    wr_en = 1'b1;    wdata = $random;    @ (posedge sys_clk);    # 2;    wr_en = 1'b0;    # 2000;    @ (negedge sdr_busy);    @ (posedge sys_clk);    # 2;    rd_en = 1'b1;    @ (posedge sys_clk);    # 2;    rd_en = 1'b0;    # 2000;    $stop;  end
endmodule


这设置激励时,将tb文件和仿真模型文件同时加入添加文件中。

 

在modelsim的报告界面会显示出具体的配置信息以及读写信息。


从打印的报告中可以看出,在初始化时,列选通潜伏期为2,突发长度为2。在后续的读写时,在指定的位置,写入了13604,后续的一个位置为4629;在读出时,也正确的读出了数据。

报告打印出写入数据,即认为写入成功;报告打印出读出数据,只能证明控制器将数据读出,并不表示控制器能把数据接收到。


通过控制输出的rdata以及对应的rd_valid信号,确定读出成功。在rdata中显示为16进制,16进制的1215为十进制的4629;16进制的3524的为十进制的13604。证明读数据接收正确。

 

  • 板级测试

编写控制器的上游模块(sdr_drive_test_crtl),控制写入和读出。在固定的地址中addr = {2'b01, 13'd128, 9'd20},写入一个固定的数字wdata = 32'h5a5aa5a5,然后读出,进行验证。


读者在进行验证时,可以采样其他的地址或者数据进行验证,且可以进行多次尝试,保证设计正确。


该模块采用状态机设计实现。


设计代码为:

`include "../rtl/sdr_drive_head.v"
module sdr_drive_test_ctrl (
  input     wire                                    clk,  input     wire                                    rst_n,
  input     wire                                    sdr_busy,  output    reg                                     wr_en,  output    reg                                     rd_en,  output    wire      [31:0]                        wdata,  input     wire                                    rd_valid,  input     wire      [31:0]                        rdata,  output    wire      [`ADDR_WIDTH - 1 : 0]         addr);
  localparam          IDLE        =   4'b0001;  localparam          WR_STATE    =   4'b0010;  localparam          RD_STATE    =   4'b0100;  localparam          TEST_DONE   =   4'b1000;
  reg                 [3:0]         c_state;  reg                 [3:0]         n_state;
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end
  always @ * begin    case (c_state)      IDLE          :   begin        if (sdr_busy == 1'b0)          n_state = WR_STATE;        else          n_state = IDLE;      end
      WR_STATE      :   begin        if (sdr_busy == 1'b0)          n_state = RD_STATE;        else          n_state = WR_STATE;      end
      RD_STATE      :   begin        if (rd_valid == 1'b1 && rdata == 32'h5a5aa5a5)          n_state = TEST_DONE;        else          n_state = RD_STATE;      end
      TEST_DONE     :   n_state = TEST_DONE;

      default       :   n_state = IDLE;    endcase  end
  assign wdata = 32'h5a5aa5a5;  assign addr = {2'b01, 13'd128, 9'd20};
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wr_en <= 1'b0;    else      if (c_state == IDLE && sdr_busy == 1'b0)        wr_en <= 1'b1;      else        wr_en <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rd_en <= 1'b0;    else      if (c_state == WR_STATE && sdr_busy == 1'b0)        rd_en <= 1'b1;      else        rd_en <= 1'b0;  end
endmodule


编写测试顶层,模块命名为sdr_drive_test,并且设置为顶层。


此模块负责例化sdr_drive和sdr_drive_test_ctrl,完成连接功能,以此测试。


代码为:

`include "../rtl/sdr_drive_head.v"
module sdr_drive_test (
  input     wire                                    clk,  input     wire                                    rst_n,
//  sdr  output    wire                                    sdr_clk,  output    wire                                    sdr_cke,  output    wire                                    sdr_cs_n,  output    wire                                    sdr_ras_n,  output    wire                                    sdr_cas_n,  output    wire                                    sdr_we_n,  output    wire      [1:0]                         sdr_bank,  output    wire      [`SDR_ADDR_WIDTH - 1 : 0]     sdr_addr,  output    wire      [1:0]                         sdr_dqm,  inout     wire      [15:0]                        sdr_dq);
  wire                                    sys_clk;  wire                                    sys_rst_n;
//  local  wire                                    sdr_busy;  wire                                    wr_en;  wire                                    rd_en;  wire      [`ADDR_WIDTH - 1 : 0]         addr;  wire      [31:0]                        wdata;  wire      [31:0]                        rdata;  wire                                    rd_valid;
  sdr_drive_test_ctrl sdr_drive_test_ctrl_inst(
      .clk                (sys_clk),      .rst_n              (sys_rst_n),
      .sdr_busy           (sdr_busy),      .wr_en              (wr_en),      .rd_en              (rd_en),      .wdata              (wdata),      .rd_valid           (rd_valid),      .rdata              (rdata),      .addr               (addr)    );
  sdr_drive sdr_drive_inst(
      .clk                (clk),      .rst_n              (rst_n),
      .sys_clk            (sys_clk),      .sys_rst_n          (sys_rst_n),
    //  local      .sdr_busy           (sdr_busy),      .wr_en              (wr_en),      .rd_en              (rd_en),      .addr               (addr),      .wdata              (wdata),      .rdata              (rdata),      .rd_valid           (rd_valid),
    //  sdr      .sdr_clk            (sdr_clk),      .sdr_cke            (sdr_cke),      .sdr_cs_n           (sdr_cs_n),      .sdr_ras_n          (sdr_ras_n),      .sdr_cas_n          (sdr_cas_n),      .sdr_we_n           (sdr_we_n),      .sdr_bank           (sdr_bank),      .sdr_addr           (sdr_addr),      .sdr_dqm            (sdr_dqm),      .sdr_dq             (sdr_dq)    );
endmodule


经过综合分析后,进行分配管脚。在分配管脚后,需要将双功能管脚中的NCEO设置为普通用户IO。如果不设置,将会出现如下错误:

 

右击器件名称,选择DEVICE。

 

选择device and pin option。

 

选择dual – purpose pins。


将nceo设置为 use as regular IO。


点击OK,进行编译即可。

 

连接上开发板,启动逻辑分析仪。


将采样时钟选择为,sys_clk(PLL的c0)。采样深度选择为1K。

 

添加观测信号如下,将wr_en的上升沿设置为触发条件。

经过保存,重新形成配置文件后,进行下板测试。


下板后,按下复位。等待波形触发。

通过逻辑分析仪,就可以看出可以正确的写入和读出数据。


读者也可以进行尝试一次性写入多个数据,然后进行读出,进行验证设计的正确性。


福利】:QQ交流群173560979,进群备注名字+学校/企业。
淘宝店铺:https://shop588964188.taobao.com
论坛网址:www.sxznfpga.com
叁芯智能FPGA课程

END


往期精选 

图片 
图片 

【免费】FPGA工程师招聘平台

今日说“法”:FPGA芯片如何选型?

SANXIN-B01开发板verilog教程V3电子版

学员笔记连载 | FPGA Zynq 千兆以太网回环

就业班学员学习笔记分享:FPGA之HDMI、以太网篇

求职面试 | FPGA或IC面试题最新汇总篇

FPGA项目开发:204B实战应用-LMK04821代码详解(二)

项目合作 | 承接FPGA项目公告

图片
图片

FPGA技术江湖广发江湖帖

无广告纯净模式,给技术交流一片净土,从初学小白到行业精英业界大佬等,从军工领域到民用企业等,从通信、图像处理到人工智能等各个方向应有尽有,QQ微信双选,FPGA技术江湖打造最纯净最专业的技术交流学习平台。


FPGA技术江湖微信交流群

图片

加群主微信,备注姓名+公司/学校+岗位/专业进群


FPGA技术江湖QQ交流群

图片

备注姓名+公司/学校+岗位/专业进群

图片

FPGA技术江湖 任何技术的学习就好比一个江湖,对于每一位侠客都需要不断的历练,从初入江湖的小白到归隐山林的隐世高人,需要不断的自我感悟自己修炼,让我们一起仗剑闯FPGA乃至更大的江湖。
评论 (0)
  •   高海拔区域勤务与装备保障调度系统平台解析   北京华盛恒辉高海拔区域勤务与装备保障调度系统平台专为高海拔特殊地理环境打造,致力于攻克装备适应、人员健康保障、物资运输及应急响应等难题。以下从核心功能、技术特点、应用场景及发展趋势展开全面解读。   应用案例   目前,已有多个高海拔区域勤务与装备保障调度系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润高海拔区域勤务与装备保障调度系统。这些成功案例为高海拔区域勤务与装备保障调度系统的推广和应用提供了有力支持。   一、核心
    华盛恒辉l58ll334744 2025-04-24 10:13 116浏览
  •   陆地装备体系论证与评估综合平台系统解析   北京华盛恒辉陆地装备体系论证与评估综合平台系统是契合现代军事需求而生的专业系统,借助科学化、智能化手段,实现对陆地装备体系的全方位论证与评估,为军事决策和装备发展提供关键支撑。以下从功能、技术、应用及展望展开分析。   应用案例   目前,已有多个陆地装备体系论证与评估综合平台在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润陆地装备体系论证与评估综合平台。这些成功案例为陆地装备体系论证与评估综合平台的推广和应用提供了有力支持。
    华盛恒辉l58ll334744 2025-04-24 10:53 125浏览
  •   通用装备论证与评估系统平台解析   北京华盛恒辉通用装备论证与评估系统平台是服务军事装备全生命周期管理的综合性信息化平台,通过科学化、系统化手段,实现装备需求论证、效能分析等核心功能,提升装备建设效益。   应用案例   目前,已有多个通用装备论证与评估系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润通用装备论证与评估系统。这些成功案例为通用装备论证与评估系统的推广和应用提供了有力支持。   一、系统分层架构   (一)数据层   整合装备性能、作战、试验等多源异
    华盛恒辉l58ll334744 2025-04-24 16:14 130浏览
  • 引言:语音交互的智能化跃迁在全球化与智能化深度融合的今天,语音交互设备的应用场景已从单一提示功能向多语言支持、情感化表达及AI深度交互演进。传统离线语音方案受限于语种单一、存储容量不足等问题,而纯在线方案又依赖网络稳定性,难以满足复杂场景需求。WT3000A离在线TTS方案,通过“本地+云端”双引擎驱动,集成16国语种、7种方言切换、AI大模型对话扩展等创新功能,重新定义语音提示器的边界,为智能硬件开发者提供更灵活、更具竞争力的语音交互解决方案。一、方案核心亮点离在线双模融合,场景全覆盖离线模式
    广州唯创电子 2025-04-25 09:14 31浏览
  •   海上训练与保障调度指挥平台系统解析   北京华盛恒辉海上训练与保障调度指挥平台系统是现代海上作战训练的核心枢纽,融合信息技术、GIS、大数据及 AI 等前沿技术,旨在实现海上训练高效组织、作战保障科学决策。以下从架构功能、应用场景、系统优势及发展挑战展开解读。   应用案例   目前,已有多个海上训练与保障调度指挥平台在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润海上训练与保障调度指挥平台。这些成功案例为海上训练与保障调度指挥平台的推广和应用提供了有力支持。   一
    华盛恒辉l58ll334744 2025-04-24 15:26 127浏览
  •   电磁频谱数据综合管理平台系统解析   一、系统定义与目标   北京华盛恒辉电磁频谱数据综合管理平台融合无线传感器、软件定义电台等前沿技术,是实现无线电频谱资源全流程管理的复杂系统。其核心目标包括:优化频谱资源配置,满足多元通信需求;运用动态管理与频谱共享技术,提升资源利用效率;强化频谱安全监管,杜绝非法占用与干扰;为电子战提供频谱监测分析支持,辅助作战决策。   应用案例   目前,已有多个电磁频谱数据综合管理平台在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润电磁频谱数
    华盛恒辉l58ll334744 2025-04-23 16:27 209浏览
  •   后勤实验仿真系统平台深度解析   北京华盛恒辉后勤实验仿真系统平台依托计算机仿真技术,是对后勤保障全流程进行模拟、分析与优化的综合性工具。通过搭建虚拟场景,模拟资源调配、物资运输等环节,为后勤决策提供数据支撑,广泛应用于军事、应急管理等领域。   应用案例   目前,已有多个后勤实验仿真系统平台在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润后勤实验仿真系统平台。这些成功案例为后勤实验仿真系统平台的推广和应用提供了有力支持。   一、核心功能   (一)后勤资源模拟
    华盛恒辉l58ll334744 2025-04-23 15:39 188浏览
  • 引言在智能语音技术飞速发展的今天,语音交互已成为消费电子、智能家居、工业控制等领域的标配功能。传统的ISD系列录音芯片虽应用广泛,但其高成本与功能局限性逐渐难以满足市场对高性价比、高灵活性的需求。推出的WT2000P录音语音芯片,凭借其卓越性能、低功耗设计及高度可定制化特性,成为ISD系列芯片的理想替代方案,助力开发者突破产品创新瓶颈。一、WT2000P产品概述WT2000P是一款专为嵌入式语音场景设计的多功能录音芯片,采用ESOP8封装,体积小巧(尺寸仅4.9mm×3.9mm),集成度高,支持
    广州唯创电子 2025-04-25 08:44 19浏览
  •   航空兵训练与战术对抗仿真平台系统解析   北京华盛恒辉航空兵训练与战术对抗仿真平台系统是现代军事训练的关键工具,借助计算机技术构建虚拟战场,支持多兵种协同作战模拟,为军事决策、训练及装备研发提供科学依据。   应用案例   目前,已有多个航空兵训练与战术对抗仿真平台在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润航空兵训练与战术对抗仿真平台。这些成功案例为航空兵训练与战术对抗仿真平台的推广和应用提供了有力支持。   一、系统架构与核心功能   系统由模拟器、计算机兵力生
    华盛恒辉l58ll334744 2025-04-24 16:34 141浏览
  • 随着轻薄笔记本的普及,再加上电竞玩家对于高画质音视频体验的需求日益高涨,如何让轻薄笔记本在兼顾轻便携带性的同时,还能提供足以支持3A(AAA/Triple-A game)大作的良好运算性能,便成为各家品牌急欲突破的共同难题。然而,对于主打轻巧便携的轻薄笔记本而言,若要内置独立显卡,势必要先突破空间受限的瓶颈,同时还需解决散热问题,确实难以兼顾两全!对此,“Thunderbolt”与“OCuLink”这两项技术应运而生。用户可以通过这两种传输接口,再搭配外接显卡盒(eGPU)及高性能显卡(如NVI
    百佳泰测试实验室 2025-04-24 17:56 27浏览
  • 2025-4-25全球信息报告出版商Global Info Research(环洋市场咨询)发布了【2025年全球市场高介电常数材料总体规模、主要生产商、主要地区、产品和应用细分研究报告】,报告主要调研全球高介电常数材料总体规模、主要地区规模、主要生产商规模和份额、产品分类规模、下游主要应用规模以及未来发展前景预测。统计维度包括销量、价格、收入,和市场份额。同时也重点分析全球市场主要厂商(品牌)产品特点、产品规格、价格、销量、销售收入及发展动态。历史数据为2020至2024年,预测数据为2025
    用户1745398400862 2025-04-25 08:48 31浏览
  • 最近,途虎养车发布的2024年财报数据,可谓相当吸睛。全年营收达到147.59亿元,同比增长8.5%,这个数字直观地展现了途虎在市场上的强大吸金能力,在行业里稳稳占据前列。利润方面同样出色,毛利37.46亿元,毛利率提升0.7个百分点至25.4%;经调整净利润6.24亿元,同比增长 29.7%,经营利润同比更是增长104%至3.31亿元,盈利能力显著增强,这样的利润增长幅度,在同行业中十分亮眼。在用户规模上,途虎养车同样成绩斐然。累计注册用户近1.4亿,同比增长20.4%,交易用户数达2410万
    用户1742991715177 2025-04-24 19:12 28浏览
  • 为通过金融手段积极推进全球绿色发展,国际金融论坛(IFF)于2020年创立了“IFF全球绿色金融奖”,旨在对全球绿色金融领域取得突出成绩的机构及创新性的解决方案进行表彰和奖励。该奖项依托IFF“高层次、高水平、国际化”一流智库资源优势,积极促进绿色金融领域的国际交流合作和创新实践,助力联合国可持续发展目标的实现。“IFF全球绿色金融奖”重点关注和鼓励那些促进经济增长模式转型、防治环境污染、应对气候变化,以及致力于提高能效水平、强化节能减排实效的绿色金融创新解决方案。该奖项面向全球,是对政策创新、
    华尔街科技眼 2025-04-24 20:43 14浏览
  •   有效样本分析决策系统平台全面解析   一、引言   北京华盛恒辉有效样本分析决策系统在当今数据驱动的时代,企业、科研机构等面临着海量数据的处理与分析挑战。有效样本分析决策系统平台应运而生,它通过对样本数据的精准分析,为决策提供有力支持,成为提升决策质量和效率的关键工具。   应用案例   目前,已有多个有效样本分析决策系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润有效样本分析决策系统。这些成功案例为有效样本分析决策系统的推广和应用提供了有力支持。   二、平台概述
    华盛恒辉l58ll334744 2025-04-24 11:13 114浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦