ZYNQ学习:Verilog语言心得(一)

学习使用RAM IP核的时候遇到了问题,编写的代码与想象的不一致,具体表现为时序滞后一拍,下面是问题的解决思路。

设计波形图:

如图rw_cnt信号应该随着ram_en信号的拉高开始工作,然后跟随计数器变动

RTL部分代码如下:

module ram_rw(
    
    input            clk,          //时钟输入
    input            rst_n,        //系统复位信号
    input      [8:0] ram_rd_data,  //RAM读来的数据
                  
    output reg       ram_en,       //RAM总使能信号,高电平有效
    output reg       ram_we,       //RAM写使能信号,高电平有效
    output reg [4:0] ram_addr,     //RAM地址,读写公用
    output reg [8:0] ram_wr_data   //向RAM写的数据
    );
    
reg [5:0] rw_cnt;

//对RAM_EN使能信号进行赋值,结束复位之后拉高
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        ram_en <= 1'b0;
    else
        ram_en <= 1'b1;
    end
   
//实现模64计数器,以便实现以下三个输出信号:
//ram_we,
//ram_addr,
//ram_wr_data
always @(posedge clk or negedge rst_n) begin
    if(!rst_n || !ram_en)
        rw_cnt <= 6'b0;
    else
        rw_cnt <= rw_cnt+6'b1;  //计数满后自动清零
    end

//设置ram_we信号,在计数0-31激活写入,32-63激活读取
always @(posedge clk or negedge rst_n) begin
    if(!rst_n || !ram_en)
        ram_we <= 1'b0;
    else
        ram_we <= (rw_cnt <= 6'd31);  //31以下是计数器的值,31以上是计数器的值减去32
    end

//设置地址信号,与计数器有关
always @(posedge clk or negedge rst_n) begin
    if(!rst_n || !ram_en)
        ram_addr <= 8'b0;
    else
        ram_addr <= (rw_cnt <= 6'd31) ? {2'b0,rw_cnt} : {2'b0,rw_cnt-6'd32};  
//31以下是计数器的值,31以上是计数器的值减去32
    end

 //设置写数据输出,与计数器有关
always @(posedge clk or negedge rst_n) begin
    if(!rst_n || !ram_en || !ram_we)
        ram_wr_data <= 8'b0;
    else
        ram_wr_data <= rw_cnt;  //在可以写入的时候写入值与计数器值相等
    end

endmodule

实际仿真结果:

可以看到ram_we信号滞后了一个周期才开始工作,观察RTL代码可以发现,我们使用always赋值语句块来实现的ram_we,所以在这里信号延后了

//设置ram_we信号,在计数0-31激活写入,32-63激活读取
always @(posedge clk or negedge rst_n) begin
    if(!rst_n || !ram_en)
        ram_we <= 1'b0;
    else
        ram_we <= (rw_cnt <= 6'd31);  //31以下是1,31以上是0
    end

        也就是说,在时钟上升沿触发该always语句块时(第一条蓝色线),因为ram_en处于跳变状态,所以判断结果为跳变之前的值(重要),即ram_en为低电平,所以触发if语句,ram_we为低电平。

        直到下个时钟上升沿(第二条蓝色线),ram_en已经稳定为低电平,所以这时候ram_we才开始变为高电平有效状态。如果需要解决这个延后问题,就需要把ram_en改为组合逻辑。

另外观察到VIVADO出现了高阻状态的线,这一般与没接好线有关,仔细检查后发现RTL代码有误

这里全部写错了应该是[7:0]表示8位

修改RTL代码如下:

//设置ram_we信号,在计数0-31激活写入,32-63激活读取
assign ram_we = (!rst_n || !ram_en) ? 1'b0 : (rw_cnt <= 6'd31);

重新运行仿真

可以观察到根据计数器产生的信号也都延后了计数器一拍,这说明我们不能这样基于一个计数器设计,需要每个信号重新计数,每个信号只延后rst_n和ram_en一个周期。

重新设计RTL代码,使其与计数器无关,并且修改数位问题:

module ram_rw(
    
    input            clk,          //时钟输入
    input            rst_n,        //系统复位信号
    input      [7:0] ram_rd_data,  //RAM读来的数据
                  
    output reg       ram_en,       //RAM总使能信号,高电平有效
    output           ram_we,       //RAM写使能信号,高电平有效
    output reg [4:0] ram_addr,     //RAM地址,读写公用
    output reg [7:0] ram_wr_data   //向RAM写的数据
    );
    
reg [5:0] rw_cnt;

//对RAM_EN使能信号进行赋值,结束复位之后拉高
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        ram_en <= 1'b0;
    else
        ram_en <= 1'b1;
    end
   
//实现模64计数器,以便实现以下三个输出信号:
//ram_we,
//ram_addr,
//ram_wr_data
always @(posedge clk or negedge rst_n) begin
    if(!rst_n || !ram_en)
        rw_cnt <= 6'b0;
    else
        rw_cnt <= rw_cnt+6'b1;  //计数满后自动清零
    end

//设置ram_we信号,在计数0-31激活写入,32-63激活读取,即时随ram_en信号变化
assign ram_we = (!rst_n || !ram_en) ? 1'b0 : (rw_cnt <= 6'd31);

//设置地址信号,相当于计数器,延后ram_en信号一拍
always @(posedge clk or negedge rst_n) begin
    if(!rst_n || !ram_en)
        ram_addr <= 8'd0;
    else if(ram_addr == 8'd31)
        ram_addr <= 8'd0;
    else
        ram_addr <= ram_addr + 8'd1;  
    end

 //设置写数据输出,延后ram_we信号一拍
always @(posedge clk or negedge rst_n) begin
    if(!rst_n || !ram_en || !ram_we)
        ram_wr_data <= 8'd0;
    else if(ram_wr_data == 8'd31)
        ram_wr_data <= 8'd0;
    else
        ram_wr_data <= ram_wr_data + 8'd1;  
    end

endmodule

此时没有任何问题。

总结

1.使用always判断时钟上升沿来临时,如果改语句块内进行了判断,则判断信号的电平是该时刻之前的电平,这会导致赋值延后一个周期

2.这是时序逻辑的特性,如果想同步变化需要使用组合逻辑

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值