一,引言

        因为在我们系统中,各个模块之间工作的时钟频率不一定一样,所以很多时候牵扯跨时钟域的数据传输,由快时钟域到慢时钟域,由慢时钟域到快时钟域,低位宽到高位宽等,例如采样系统时钟频率10MHz,数据处理系统时钟频率50MHz,此时就需要进行跨时钟域的数据传输。

二,简单双端口RAM IP核简介

如下图

简单双端口RAM的a端口为只写端口,b端口为只读端口。

各主要端口定义如下:

简单双端口RAM的A端口为只写端口,B端口为只读端口。

  1. DINA:数据输入端口,用于向 A 端口输入待写入 RAM 的数据。
  2. ADDRA:数据地址写入端口,指定 A 端口数据写入 RAM 的存储单元地址。
  3. WEA:写入使能信号,控制 A 端口是否允许在时钟边沿将数据写入 RAM。
  4. ENA:A 端口使能信号,控制 A 端口整体是否可进行写入操作。
  5. CLKA:数据写入时钟,为 A 端口数据写入操作提供时序基准。
  6. ADDRB:读数据端口,指定 B 端口从 RAM 读取数据的存储单元地址。
  7. ENB:B 端口使能信号,控制 B 端口是否可进行读取操作。
  8. CLKB:数据读出时钟,为 B 端口数据读取操作提供时序基准。
  9. DOUTB:数据输出端口,用于输出 B 端口从 RAM 读取的数据。

三、系统框图

本次实际项目为,adc采样模块以10.24MHz的速率采样,RAM模块以10.24MHz的速率写入,再以50MHz的速率读出(后面的模块速率为50MHz)

四、IP核详细配置

 

       模式选择普通模式(Native),双端口简单RAM(Simple Dual Port RAM)数据位宽设置为16位宽,数据深度为2048。

五、详细代码

1.top文件

`timescale 1ns / 1ps

module top (
    input   wire            sys_clk,
    input   wire            sys_rst_n,
    input   wire     [15:0]  ad_data,
    
    input   wire            start,

    output  wire            AD_clk
);

ADC_sample  u_ADC_sample
(
    .sys_clk     (sys_clk),
    .sys_rst_n   (sys_rst_n),

    .AD_clk      (AD_clk),
    .locked      ()    //锁定信号
);

wire                ADC_RAM_last;
wire    [15:0]      doutb;

ADC_RAM     u_ADC_RAM
(
    .ADC_RAM_clka    (AD_clk),         // 写操作时钟
    .sys_clk         (sys_clk),         // 读操作时钟
    .sys_rst_n       (sys_rst_n),         // 系统复位
    .AD_data         (ad_data),         // ADC 输入数据
      
    .start             (start),
      
    .doutb           (doutb),         // RAM 输出数据
    .ADC_RAM_last    (ADC_RAM_last)         // 读地址到达最后一个标志
                     
    
);




endmodule

2.ADC_sample.v

`timescale 1ns / 1ps

module ADC_sample
(
    input   wire            sys_clk     ,
    input   wire            sys_rst_n   ,
                                    
    output  wire            AD_clk      ,
    output  wire            locked          //锁定信号
);

AD_CLK u_AD_CLK
(
    // Clock out ports
    .clk_out1(AD_clk),     // output clk_out1
    // Status and control signals
    .reset(~sys_rst_n), // input reset
    .locked(locked),       // output locked
   // Clock in ports
    .clk_in1(sys_clk)      // input clk_in1
);
endmodule

3.ADC_RAM.v

`timescale 1ns / 1ps

module ADC_RAM
(
    input   wire            ADC_RAM_clka    , // 写操作时钟
    input   wire            sys_clk         , // 读操作时钟
    input   wire            sys_rst_n       , // 系统复位
    input   wire    [15:0]       AD_data    , // ADC 输入数据
    
    input   wire            start           , // 开始信号
                                             
    output  wire    [15:0]        doutb     , // RAM 输出数据
    output  reg            ADC_RAM_last     // 读地址到达最后一个标志
    
    
);

reg             ena;        //a端口使能
reg   [0:0]     wea;        //写使能
reg             enb;        //b端口使能(读使能)
reg   [10:0]    addra;
reg             rd_flag;    //读标记 
wire             rd_flag_clkb;    //读标记 
reg   [15:0]    dina;
reg   [10:0]    addrb;

(* keep = "true" *) reg start_meta;  //(* keep = "true" *) 防止信号被优化
(* keep = "true" *) reg start_sync;

always @(posedge ADC_RAM_clka or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        start_meta <= 1'b0;
        start_sync <= 1'b0;
    end else begin
        start_meta <= start;         // 第一级采样
        start_sync <= start_meta;   // 第二级稳定输出
    end
end

wire start_pos_edge = (!start_sync) && start_meta;  // 上升沿检测

reg wr_started;            

always @(posedge ADC_RAM_clka or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        addra <= 11'd0;
        rd_flag <= 1'b0;
        wr_started <= 1'b0;
    end else begin
        if (start_pos_edge && !wr_started) begin
            // 第一次检测到 rd_flag_clkb 拉高,启动读地址计数
            addra <= 11'd0;         // 或者从 0 开始
            wr_started <= 1'b1;
            rd_flag <= 1'b0;
        end else if (wr_started) begin
            if (addra == 11'd2047) begin
                addra <= 11'd0;
                rd_flag <= 1'b1;
                wr_started <= 1'b0;
            end else begin
                addra <= addra + 1'b1;
                rd_flag <= 1'b0;
            end
        end else begin
            addra <= 11'd0;
            rd_flag <= 1'b0;
        end
    end
end

////addra a端口地址跟新逻辑
//always @(posedge ADC_RAM_clka or negedge sys_rst_n)
//begin 
//    if (!sys_rst_n)
//    begin
//    addra <= 11'd0;
//    rd_flag <= 1'b0;
//    end
//    else if(addra == 11'd2047)
//    begin
//    addra <= 11'd0;    
//    rd_flag <= 1'b1;  
//    end
//    else
//    begin
//    addra <= addra + 1'b1;    
//    rd_flag <= 1'b0;  
//    end   
//end    



//a端口信号使能
always @(posedge ADC_RAM_clka or negedge sys_rst_n) 
    if (!sys_rst_n) 
        ena   <=    1'b0;
//    else if(addra < 11'd2047) ///注意
//        ena   <=    1'b1;
    else
        ena <= (wr_started && addra < 11'd2047);
        
//写入信号使能
always @(posedge ADC_RAM_clka or negedge sys_rst_n) 
    if (!sys_rst_n) 
        wea   <=    1'b0;
    else if(addra < 11'd2047) //注意
        wea   <=    1'b1;
    else
        wea   <=    1'b0;

//写入数据
always @(posedge ADC_RAM_clka or negedge sys_rst_n) 
    if (!sys_rst_n)
    dina <= 15'd0;
    else if(addra < 11'd2047&& ena == 1)
    dina <= AD_data;
    else
    dina <= 15'd0; 

    
//读标记信号(在clka时钟下)
always @(posedge ADC_RAM_clka or negedge sys_rst_n) 
    if (!sys_rst_n)
    rd_flag <= 1'b0;
    else if(addra == 11'd2046)
    rd_flag <= 1'b1;
    else
    rd_flag <= 1'b0;


reg rd_flag_meta, rd_flag_sync;

always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        rd_flag_meta <= 1'b0;
        rd_flag_sync <= 1'b0;
    end else begin
        rd_flag_meta <= rd_flag;      // 第一级采样
        rd_flag_sync <= rd_flag_meta; // 第二级稳定输出
    end
end

assign rd_flag_clkb = rd_flag_sync;


//addrb b端口地址跟新逻辑
reg rd_started;

always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        addrb <= 11'd0;
        ADC_RAM_last <= 1'b0;
        rd_started <= 1'b0;
    end else begin
        if (rd_flag_clkb && !rd_started) begin
            // 第一次检测到 rd_flag_clkb 拉高,启动读地址计数
            addrb <= 11'd0;         // 或者从 0 开始
            rd_started <= 1'b1;
            ADC_RAM_last <= 1'b0;
        end else if (rd_started) begin
            if (addrb == 11'd2047) begin
                addrb <= 11'd0;
                ADC_RAM_last <= 1'b1;
                rd_started <= 1'b0;
            end else begin
                addrb <= addrb + 1'b1;
                ADC_RAM_last <= 1'b0;
            end
        end else begin
            addrb <= 11'd0;
            ADC_RAM_last <= 1'b0;
        end
    end
end
////b端口信号使能
//always @(posedge sys_clk or negedge sys_rst_n) 
//    if (!sys_rst_n) 
//        enb   <=    1'b0;
//    else if(addrb <= 11'd2047)
//        enb   <=    1'b1;
//    else
//        enb   <=    1'b0;

//reg enb_enable;

always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        enb <= 1'b0;
    end else begin
        if (rd_flag_clkb) 
            enb <= 1'b1;
        else if (ADC_RAM_last || addrb > 11'd2047) 
            enb <= 1'b0;
        else begin
            enb <= enb; // 保持状态
        end
    end
end



adc_RAM_16x2048 u_adc_RAM_16x2048 (
  .clka(ADC_RAM_clka),           // input wire clka
  .ena(ena),             // input wire ena
  .wea(wea),             // input wire [0 : 0] wea
  .addra(addra),         // input wire [10 : 0] addra
  .dina(dina),           // input wire [15 : 0] dina
  
  .clkb(sys_clk),           // input wire clkb
  .enb(enb),             // input wire enb
  .addrb(addrb),         // input wire [10 : 0] addrb
  .doutb(doutb)         // output wire [15 : 0] doutb
);

endmodule

4.dds.v

`timescale 1ns / 1ps  
// 定义时间单位:1ns 为仿真精度的基本单位,1ps 为时间分辨率
module dds(  
// 模块定义:DDS(直接数字频率合成器)模块
    input i_clk,              // 输入时钟信号
    input i_rst,              // 输入复位信号(高电平有效)

    output signed[15:0]o_sin, // 输出正弦波数据(16位有符号数)
    output signed[15:0]o_cos  // 输出余弦波数据(16位有符号数)
);

reg [31:0]addr;  
// 定义一个32位寄存器变量 addr,用于控制相位累加器的频率调谐字(Frequency Tuning Word)

always @(posedge i_clk or posedge i_rst)  
// 在时钟上升沿或复位上升沿触发
begin
     if(i_rst)  
     begin
         addr <= 32'd0;  
         // 如果复位有效,将 addr 清零
     end
     else 
     begin
         addr <= 32'd250000;  
         // 否则,将 addr 设置为固定值 50000,表示设定的频率参数
     end
end   

wire[31:0]m_axis_data_tdata;  
// 定义一个32位线网型信号,用于接收 DDS IP 核输出的数据(包含 sin 和 cos)

// 实例化 Xilinx DDS IP 核:dds_compiler_0
dds_compiler_0 dds_compiler_0(
    .aclk(i_clk),                                  // 输入时钟
    .aresetn(~i_rst),                              // 异步复位,低电平有效(取反后变为低电平复位)
    .s_axis_config_tvalid(1'b1),                   // 配置通道始终有效
    .s_axis_config_tdata(addr),                    // 频率调节字输入
    .m_axis_data_tvalid(),                         // 输出数据有效标志(未使用)
    .m_axis_data_tdata(m_axis_data_tdata),         // 输出数据总线(32位,包含 sin 和 cos)
    .m_axis_phase_tvalid(),                        // 相位输出有效标志(未使用)
    .m_axis_phase_tdata()                          // 相位输出数据(未使用)
);

assign o_sin = m_axis_data_tdata[31:16];  
// 将输出数据的高16位赋给 o_sin,表示正弦波幅值

assign o_cos = m_axis_data_tdata[15:0];  
// 将输出数据的低16位赋给 o_cos,表示余弦波幅值

endmodule  
// 模块结束

5.tb_top(仿真文件)

`timescale 1ns / 1ps

module tb_top();

reg             sys_clk     ;  
reg             sys_rst_n   ;
wire     [15:0]     sin     ;

wire     [15:0]      ad_data;
reg                  start;

assign ad_data = sin[15:0];

initial 
    begin
        sys_clk = 1'b0;
        sys_rst_n <= 1'b0;
        start = 1'b0;
        #200
        sys_rst_n <= 1'b1;
        #20
        start = 1'b1;
        #5000
        start = 1'b0;         
    end

always  #10     sys_clk =   ~sys_clk    ;   //50MHz系统时钟



dds     dds_inst
(  
                           // 模块定义:DDS(直接数字频率合成器)模块
    .i_clk(sys_clk),              // 输入时钟信号
    .i_rst(~sys_rst_n),              // 输入复位信号(高电平有效)

    .o_sin(sin),              // 输出正弦波数据(16位有符号数)
    .o_cos()               // 输出余弦波数据(16位有符号数)
);



top     top_inst
(
     .sys_clk       (sys_clk),
     .sys_rst_n     (sys_rst_n),
     .ad_data       (ad_data),
     .start           (start),

     .AD_clk        ()
);


endmodule

六、仿真结果

如上图所示,2048个数据从10.24MHz的时钟域成功传输到50MHz的时钟域。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐