我是靠谱客的博主 糟糕小懒虫,最近开发中收集的这篇文章主要介绍整理经典verilog笔试题/面试手撕代码信号跨时钟域处理序列检测:1011时钟分频7进制循环计数器(可赋初值)4位全加器边沿检测,输出一个周期宽度的脉冲串并转换实现一个异步双端口RAM,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

这里写自定义目录标题

  • 信号跨时钟域处理
    • 单个电平信号的时钟域跨越
    • 单个脉冲信号的时钟域跨越
    • 多比特总线的时钟域跨越
  • 序列检测:1011
    • 使用状态机实现
    • 使用移位寄存器实现
  • 时钟分频
  • 7进制循环计数器(可赋初值)
  • 4位全加器
  • 边沿检测,输出一个周期宽度的脉冲
  • 串并转换
  • 实现一个异步双端口RAM

信号跨时钟域处理

单个电平信号的时钟域跨越

起点时钟域由触发器来驱动信号的离开(过滤毛刺)
终点时钟域采用两个或多个触发器串联

always @ (posedge clk or negedge rst)
begin 
	if(!rst)
	begin
		data_in_1 <= 1'b0;
		data_in_2 <= 1'b0;
		out <= 1'b0;
	end
	else begin
		data_in_1 <= data_in;
		data_in_2 <= data_in_1;
		out <= data_in_2;
	end
end

单个脉冲信号的时钟域跨越

起点时钟域将脉冲转电平变化
终点时钟域使用双触发器同步,并随后检测电平变化,转换成脉冲。

//脉冲转电平
always @ (posedge clk or negedge rst)
begin
	if(!rst)
		out <= 0;
	else if(data_in)
		out <= ~out;
	else
		out <= out;
end

//电平变化检测
//电平变化检测
always @ (posedge clk, negedge rst)
begin
  if(!rst)
    data_in_1 <= 0;
  else begin
    data_in_1 <= data_in;
  end
  
end

assign out = data_in ^ data_in_1;

多比特总线的时钟域跨越

若按照单比特方法,将其按照总线位宽展开,在起点时钟域中与时钟边沿对其的数据,在到达终点时钟域后可能产生数据错位
1.格雷编码的计数器:传输计数器值可以这样
2.异步FIFO:包括简单双口读写RAM、读指针、写指针(读写指针就是顺序累加的计数器)
3.翻转同步器做数据选通脉冲:异步FIFO需要资源较多

序列检测:1011

使用状态机实现

module serchek(
  input clk,
  input rst,
  
  input in,
  output out
  );
  
  reg [2:0] now_state, next_state;
  parameter IDLE = 3'b000, A = 3'b001, B = 3'b011, C = 3'b010, D = 3'b110;
  
  always @ (posedge clk, negedge rst)   //时序部分
  begin
    if(!rst)
      now_state <= IDLE;
    else
      now_state <= next_state;
  end
  
  always @ (*)          //组合逻辑部分
  begin
    case (now_state)
      IDLE: if(in) next_state = A;
            else  next_state = IDLE;
      A:  if(in) next_state = A;
          else  next_state = B;
      B:  if(in) next_state = C;
          else next_state = IDLE;
      C:  if(in) next_state = D;
          else  next_state = B;
      D:  if(in) next_state = A;
          else next_state = IDLE;
      
    endcase
  end
  
  always @ (posedge clk, negedge rst)       //时序产生输出
  begin
    if(!rst)
      out <= 0;
    else
      if(now_state == D)
        out <= 1;
  end
  assign out = (rst) ? 0 : (now_state == D) ? 1 : 0;      //组合逻辑产生输出
endmodule

使用移位寄存器实现

module serchek(
  input clk,
  input rst,
  
  input din,
  output reg flag
  )
  
  reg [3:0] data_re;
  
  always @ (posedge clk, negedge rst)
  begin 
    if(!rst)
      data_re <= 4'd0;
    else begin 
      data_re <= {data_re[2:0], din};
    end 
  end 
  
  assign flag = (data_re == 4'b1011) ? 1'b1 : 1'b0;
endmodule

时钟分频

module div(       
  input clk,
  input rst,
  output clk_out
  )

//------------------ 二分频(两个周期变成一个周期)------------
//-------------------------------------------------------------- 
  reg clk_out;
  
  always @ (posedge clk, negedge rst)
  begin
    if(!rst)
      clk_out <= 0;
    else
      clk_out <= ~clk_out;
  end
  
//------------------ 三分频(三个周期变成一个周期)------------
//-----------------------占空比2/3------------------------------
  reg clk_out;
  reg [1:0]count;
  
  always @ (posedge clk, negedge rst)
  begin 
    if(!rst)
    begin
      clk_out <= 0;
      count <= 2'b00;       //利用计数器进行信号翻转计数
    end
    else if (count == 0)
    begin
      clk_out <= ~clk_out;
      count <= count + 1'b1; //!!!!!!
    end
    else if (count == 2)
    begin
      clk_out <= ~clk_out;
      count <= 2'b00;
    end
    else begin
      count <= count + 1'b1;
    end      

//------------------ 三分频(三个周期变成一个周期)------------
//-----------------------占空比1/2------------------------------
//---------利用两个占空比为1/3(上升沿和下降沿)进行或操作----

  reg clk_out, clk1, clk2;
  reg [1:0]count1, count2;

  always @ (posedge clk, negedge rst)   //上升沿触发
  begin 
    if(!rst)
    begin 
      clk1 <= 1'b0;
      count1 <= 2'b00;
    end 
    else if(count1 == 0)
    begin 
      clk1 <= ~clk1;
      count1 <= count1 + 2'b01;
    end 
    else if(count1 == 1)
    begin 
      clk1 <= ~clk1;
      count1 <= count1 + 2'b01;
    end 
    else if(count1 == 2)      // 直接else begin即可。
    begin 
      count1 <= 2'b00;
    end 
  end
  
  always @ (negedge clk, negedge rst)   //下降沿触发
  begin 
    if(!rst)
    begin 
      clk2 <= 1'b0;
      count2 <= 2'b00;
    end 
    else if(count2 == 0)
    begin 
      clk2 <= ~clk1;
      count2 <= count2 + 2'b01;
    end 
    else if(count2 == 1)
    begin 
      clk2 <= ~clk1;
      count2 <= count2 + 2'b01;
    end 
    else if(count2 == 2)      // 直接else begin即可。
    begin 
      count2 <= 2'b00;
    end 
  end
  
  assign clk_out = clk1 | clk2;

endmodule

7进制循环计数器(可赋初值)

module count(
  input clk,
  input rst,
  
  input [2:0]init,
  input en, 
  
  output [2:0] y
  )
  
  reg [2:0] y;
  
  always @ (posedge clk, negedge rst)
  begin 
    if(!rst)
      y <= 3'b000;
    else begin
      if(en)
        y <= init;
      else if(y == 3'b110)        //应该改成 >= 判断,防止初始值超过阈值
        y <= 3'b000;
      else 
        y <= y + 3'b001;
    end
  end
end 

4位全加器

module adder(
  input clk,
  input rst,
  
  input [3:0] a,b;
  input ci,               //包含输入的进位和输出的进位
  output [3:0] y,
  output co
  )
  
  always @ (posedge clk, negedge rst)
  begin 
    if(!rst)
      {co,y} <= 5'b00000;
    else begin 
      {co,y} <= a + b + ci;
  end 
end 

边沿检测,输出一个周期宽度的脉冲

module edge_detect(
  input clk,
  input rst,
  
  input data,
  output pos_det,
  output neg_det,
  output posneg_det
  )
  
  reg [1:0] data_re;                  //对输入的连续两个信号进行保留
  always @ (posedge clk, negedge rst)
  begin 
    if(!rst)
      data_re <= 2'b00;
    else 
      data_re <= {data_re[0], data};
  end 
  
  assign pos_det = ~data_re[1] & data_re[0];     //对上升沿的判断
  assign neg_det = data_re[1] & ~data_re[0];
  assign posneg_det = pos_det | neg_det;
endmodule

串并转换

//--------------------------- 串行转并行(4bit) -----------------
module ser2pal(
  input clk,
  input rst,
  
  input in,
  output [3:0] out_lsb,
  output [3:0] out_msb
  )
  
  always @ (posedge clk, negedge rst)
  begin 
    if(!rst)
    begin 
      out_lsb <= 4'b0000;
      out_msb <= 4'b0000;
    end 
    else begin 
      out_lsb <= {out_lsb[2:0], in};
      out_msb <= {in, out_msb[3:1]};
    end 
  end
end 

//--------------------------- 并行转串行(4bit) -----------------
module pal2ser (
  input clk,
  input rst,
  
  input [3:0] in,
  input en,         //需要一个使能将并行数据置位一下
  output out
  )
  
  rsg [3:0] in_re;
  
  always @ (posedge clk, negedge rst)
  begin 
    if(!rst)
      in_re <= 4'b0000;
    else begin 
      if(en)
        in_re <= in;
      else 
        in_re <= in_re << 1;
  end 
  
  assign out = in_re[3];
endmodule

实现一个异步双端口RAM

深度为16,位宽为8bit,A口读,B口写,支持片选、读写请求

module ram_16x8
  #( 
  parameter ADDR_WIDTH = 4,
  parameter DATA_WIDTH = 8,
  parameter NUMBER_REG = 1 << ADDR_WIDTH
  )  
  (
  input rst,
  input csen,
  
  input clk1,
  input ren,
  input [ADDR_WIDTH-1:0] addr1,
  output reg [DATA_WIDTH-1:0] dout1,
  
  input clk2,
  input wen,
  input [ADDR_WIDTH-1:0] addr2,
  input [DATA_WIDTH-1:0] din2
  );
  
  reg [DATA_WIDTH-1:0] register [NUMBER_REG-1:0];
  integer i;
  
  always @ (posedge clk1, negedge rst)
  begin 
    if(!rst)
      dout1 <= 0; 
    else if(ren && !csen)
      dout1 <= register[addr1];
    else 
      dout1 <= dout1;
  end 
  
  always @ (posedge clk2 or negedge rst) 
  begin
    if(!rst) begin          //对整个ram进行初始化
      for(i=0;i<NUMBER_REG;i=i+1) begin
        register[i] <= 0;
      end
    end
    else begin
      if(wen && csen)
        register[addr2] <= din2; 
    end
  end

最后

以上就是糟糕小懒虫为你收集整理的整理经典verilog笔试题/面试手撕代码信号跨时钟域处理序列检测:1011时钟分频7进制循环计数器(可赋初值)4位全加器边沿检测,输出一个周期宽度的脉冲串并转换实现一个异步双端口RAM的全部内容,希望文章能够帮你解决整理经典verilog笔试题/面试手撕代码信号跨时钟域处理序列检测:1011时钟分频7进制循环计数器(可赋初值)4位全加器边沿检测,输出一个周期宽度的脉冲串并转换实现一个异步双端口RAM所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(40)

评论列表共有 0 条评论

立即
投稿
返回
顶部