面试常见手撕代码题
✍️

面试常见手撕代码题

标签&分类
Verilog
发表时间
Jun 18, 2022 01:40 PM
描述
IC设计
属性
属性 1
属性 2
日期
Property
notion image
 

写1:100质数

c语言
//C
#include<stdio.h>
int main () 
{
		int i;
		int j;
		int flag = 1;
		
		for(i = 2; i <= 100; i++)
		{
				for(j = 2;j <= i/2; j++)
				{
						if(i % j == 0)
						{
							flag = 0;
							break;
						}
				}
						if(flag == 1)
						{
								printf("%d\n",i);
						}
		flag = 1;
		}	
return 0;
}
Matlab
for i = 2:100         %外层循环,i的初始值是2,终值是100
		for j = 2:100     %内层循环,j的初值是2,终值100
				if(~mod(i,j)) %i除以j取余后再取反
				    break;    %跳出循环
				end
		end
		if(j > (i/j))
			fprintf('%d is prime \n ',i);
		end
end 

SV中的约束,怎么将256个数从小到大排列,不用sort方法

//分析题意,256=2^8,
constraint c {for(int i=0;i<7,i++)//定义一个i(0~8)
		for(int j=i+1;j<8:i++)//定义一个j变量(0~8)
				if(array[i]>array[j]) begin//如果队列第i个元素小于第j个
						int temp = arrays[i];
								arrays[i] = arrays[j];
								arrays[j] = temp;//那么i和j调换位置
				end
}
 

分频

分频器的原理:当计数器是分频系数的一半时,信号翻转。当复位信号变化时,就会变化。
思路:需要使用always判定计数器,输入有 复位信号,时钟信号,输出是寄存器当复位信号不变时,且输入是0时,输出也是0.当复位信号不变,输入小于分频数时,计数器加1.0到3的计数器,到3翻转,就可以得到一个4分频的计数器。
notion image

奇数分频

分别写两个计数器和分频器(分别在上升沿计数、下降沿计数,上升沿分频和下降沿分频)
三分频
module divtest(
		input  clk,
		input  rst_n,
		output clk_divider
);
		reg [2:0] count_p;//最大计数到3
		reg [2:0] count_n;
		
		reg clk_p;
		reg clk_n;

parameter N = 3;
//-----------------上升沿计数----------------
always @(posedge clk or negedge rst_n)begin
		if(!rst_n)
				count_p <= 3'b0;//rst不变时为0
		else if(count_p == N-1)
				count_p <= 3'b0;
		else
				count_p <= count_p + 1'b1; 
end
//-----------------上升沿分频----------------
always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
				clk_p <= 1'b1;
		end
		else begin
				if(count_p == (N-1)/2) || (count_p == N-1)begin
						clk_p <= ~clk_p;
				end
		end
end

//-----------------下降沿计数----------------
always @(negedge clk or negedge rst_n)begin
		if(!rst_n)
				count_n <= 3'b0;//rst后为0
		else if(count_n == N-1)//重置为0
				count_n <= 3'b0;
		else
				count_n <= count_n + 1'b1;//下降沿计数并加1 
end
//-----------------下降沿分频----------------
always@(negedge clk or negedge rst_n)begin
		if(!rst_n)begin
				clk_n <= 1'b1;
		end
		else begin
				if(count_n == (N-1)/2) || (count_n == N-1)begin //逻辑或运算
						clk_n <= ~clk_n;
				end
		end
end

assgin clk_divider = clk_n | clk_p; 按位或,只要分频出现,clk_divider就会拉高

endmodule

五分频 

 由于奇分频需要保持分频后的时钟占空比为 50% ,所以不能像偶分频那样直接在分频系数的一半时使时钟信号翻转(高电平一半,低电平一半)。在此我们需要利用输入时钟上升沿和下降沿来进行设计。
接下来我们设计一个 5 分频的模块,设计思路如下:
采用计数器 cnt1 进行计数,在时钟上升沿进行加 1 操作,计数器的值为 0、1 时,输出时钟信号 clk_div 为高电平;计数器的值为2、3、4 时,输出时钟信号 clk_div 为低电平,计数到 5 时清零,从头开始计数。我们可以得到占空比为 40% 的波形 clk_div1。
采用计数器 cnt2进行计数,在时钟下降沿进行加 1 操作,计数器的值为 0、1 时,输出时钟信号 clk_div 为高电平;计数器的值为2、3、4 时,输出时钟信号 clk_div 为低电平,计数到 5 时清零,从头开始计数。我们可以得到占空比为 40% 的波形 clk_div2。      
clk_div1 和clk_div2 的上升沿到来时间相差半个输入周期,所以将这两个信号进行或操作,即可得到占空比为 50% 的5分频时钟。程序如下:

module divider(
    input  clk,
    input  rst_n,
    output clk_div
);
    
    reg clk_div;

    parameter N = 5;

    reg[2:0] cnt1;
    reg[2:0] cnt2;
    reg    clk_div1, clk_div2;

always @(posedge clk or negedge rst_n)
    if(!rst_n)
        cnt1 <= 0;
    else if(cnt1 < N-1)
        cnt1 <= cnt1 + 1'b1;
    else
        cnt1 <= 0;

always @(posedge clk or negedge rst_n)
    if(!rst_n)
        clk_div1 <= 1'b1;
    else if(cnt1 < N/ 2)
        clk_div1 <= 1'b1;
    else
        clk_div1 <= 1'b0;

always @(negedge clk or negedge rst_n)
    if(!rst_n)
       cnt2 <= 0;
    else if(cnt2 < N-1)
       cnt2 <= cnt2 + 1'b1;
    else
       cnt2 <= 0;

always @(negedge clk or negedge rst_n)
    if(!rst_n)
        clk_div2 <= 1'b1;
    else if(cnt2 < N / 2)
        clk_div2 <= 1'b1;
    else
        clk_div2 <= 1'b0;

    assign clk_div = clk_div1 | clk_div2;
endmodule
//tb
module divider_tb();
    reg clk;
    reg rst_n;
    wire clk_div;
    parameter DELY=100;
divider U_divider(
    .clk    (clk    ),
    .rst_n    (rst_n    ),
    .clk_div(clk_div)
);
    always #(DELY/2) clk=~clk;//产生时钟波形
initial begin
    $fsdbDumpfile("divider_odd.fsdb");
    $fsdbDumpvars(0,U_divider);
 end
 initial begin
          clk=0;rst_n=0;
    #DELY rst_n=1;
    #((DELY*20)) $finish;
end
endmodule
notion image
 

偶数分频

八分频代码,N分频就是N,偶数分频的时候写一个N位的计数器,再写一个分频器吗,在(N/2)-1和N-1的时候翻转
module divider8(
		input clk,
		input rst_n,
		input dividerclk
);
		reg [3:0] count_n;//位宽为3,计数最大为7
		parameter N = 8;
		//写一个计数器
		always@(posedge clk negedge rst_n)begin
				if(!rst_n)
					  count_n <= 1'b0;
			  else if(count_n == N-1)
					  count_n <= 1'b0;
				else
						count_n <= count_n + 1'b1;//从0开始计数,0~7总共八个上升沿
				end                                                                                                               
		end
		//分频器
		always@(posedge clk or negedge rst_n)
				if(!rst_n)begin
						dividerclk <= 1'b1;
				end
				else if(count_n == N/2-1) | (count_n == N-1)begin//在第四上升沿和第八上升沿跳转
						dividerclk <= ~dividerclk;
				end
		end
		
endmodule
六分频波形
notion image

任意占空比

现在在前面两个实验的基础上做一个简单的总结,实现对一个频率的任意占空比的任意分频。
比如: FPGA系统时钟是50M Hz,而我们要产生的频率是880Hz,那么,我们需要对系统时钟进行分频。很容易想到用计数的方式来分频:50000000/880 = 56818。
显然这个数字不是2的整幂次方,那么我们可以设定一个参数,让它到56818的时候重新计数就可以实现了。程序如下:
//rtl
module div(
    clk,
    rst_n,
    clk_div
);
    input clk,rst_n;
    output clk_div;
    reg clk_div;

    reg [15:0] counter;

always @(posedge clk or negedge rst_n)
    if(!rst_n)
        counter <= 0;
    else if(counter==56817)
        counter <= 0;
    else
        counter <= counter+1;

   assign clk_div = counter[15];
endmodule
下面我们来算一下它的占空比:
我们清楚地知道,这个输出波形在counter为0到32767(2的14次方)的时候为低,在32768到56817的时候为高,占空比为40%多一些,
如果我们需要占空比为50%,那么我们需要再设定一个参数,使它为56817的一半,使达到它的时候波形翻转,就可以实现结果了。
程序如下:28408=56818/2-1,计数到28408就清零,翻转,其余的计数期间,保持不变。
//rtl
module div(
    clk,
    rst_n,
    clk_div
);
    input clk,rst_n;
    output clk_div;
    reg clk_div;
    reg [14:0] counter;
always @(posedge clk or negedge rst_n)
    if(!rst_n)
        counter <= 0;
    else if(counter==28408)
        counter <= 0;
    else
        counter <= counter+1;

always @(posedge clk or negedge rst_n)
    if(!rst_n)
        clk_div <= 0;
    else if(counter==28408)
        clk_div <= ~clk_div;
endmodule
继续让我们来看如何实现任意占空比,比如还是由50M分频产生880Hz,而分频得到的信号的占空比为30%。 56818×30%=17045
//rtl
module div(
    clk,
    rst_n,
    clk_div,
    counter
);
    input clk,rst_n;
    output clk_div;
    reg clk_div;
    output [15:0] counter;
    reg [15:0] counter;

always @(posedge clk)
    if(!rst_n)
        counter <= 0;
    else if(counter==56817)
        counter <= 0;
    else counter <= counter+1;

always @(posedge clk)
  if(!rst_n)
    clk_div <= 0;
  else if(counter<17045)
    clk_div <= 1;
  else
    clk_div <= 0;
 endmodule

小数分频

输入频率f_in、输出频率 f_out,计数器位宽N、计数器步长M之间存在f_in/f_out=(2^N)/M的关系,以M为计数步长进行计数,当计数值大于等于(2^N)/2时为高电平否则为低电平。
always@(posedge clk or negedge rst_n)   
 if(~rst_n)
        clk_cnt <= 'h0;
 else if(&clk_cnt)
        clk_cnt <= 'h0;
 else
        clk_cnt <= clk_cnt+32'haaaa_aaaa;
 
always@(posedge clk or negedge rst_n)   
 if(~rst_n)
        clk_div <= 1'b0;
 else if(clk_cnt>=32'h7fff_ffff)
        clk_div<=1'b1;
 else
        clk_div<=1'b0;
notion image

序列检测器(输出)

周期性输出一个序列为00_1011_0111
module xlgen(
		input  clk,
		input  rst_n,
		output Q
);
		reg Q;
		reg [9:0] Q_r;

		always@(posedge clk or negedge rst_n)begin
				if(!rst_n)begin
						Q   <= 1'b0;
						Q_r <= 10'b0010110111;
				end
				else begin
						Q      <= Q_r[9];//最高位赋值给Q
						Q_r    <= Q_r<<1;//Q_r左移一位
						Q_r[0] <= Q;    //Q赋值给Q_r的最低位,实现输出循环
				end
				
		end
		
endmodule
module sequence(
input clk,
input rst_n,
output out
);

reg [9:0] sequence;

always@(posedge clk or negedge rst_n) begin
	if(!rst_n)
		sequence <= 10'b0010110111;
	else
		sequence <= {sequence[8:0],sequence[9] };
end

assign out = sequence[9] ;

endmodule

序列检测

序列检测器的逻辑功能就是将一个指定的比特序列从一串较长的比特流中识别出来。
例如:针对一个较长的比特流010010010011110101010…,我们希望能将比特序列为“10010”的序列检测出来,并且每次检测到10010就将输出置“1”.
注意:如序列”100100100…",根据以上的介绍,会在第六个比特的时刻输出会置1,但是,在第9个比特时输出不会被置”1“,虽然第4-8比特也构成了”10010“,但是却与第一次的”10010“重叠,因此不能算作有效检测。
二段式FSM描述:
鉴于一段式描述的缺点,两段式描述使用2个always块,一个always块利用同步时序逻辑描述状态转移,一个always块利用组合逻辑描述状态转移条件以及输出;(但是输出一般使用组合逻辑描述,而组合逻辑易产生毛刺等不稳定因素,并影响系统执行速率)
三段式FSM描述:
三段式描述应运而生,通过使用3个always块,一个always块利用同步时序逻辑描述状态转移,一个always块利用组合逻辑描述状态转移条件,一个always块利用同步时序逻辑描述输出,避免了前两种描述方式的弊端;
notion image
module SeqDetecter(
	clk,
	reset_n,
	bits,
	Det_Done
	);
	
	input	      clk;
	input	      reset_n;
	input	      bits;
	output	reg Det_Done;
	
	parameter	S0 = 5'b00001;
	parameter	S1 = 5'b00010;
	parameter	S2 = 5'b00100;
	parameter	S3 = 5'b01000;
	parameter	S4 = 5'b10000;
	
	reg		[4:0] state;
	
	always @(posedge clk or negedge reset_n)//同步时序描述状态转移和转移条件
	if(!reset_n) 
	   state <= 5'b00001;
	else begin
	   case(state)
	         S0:     if(bits == 0) state<=S0; else  state<=S1;
	         S1:     if(bits == 0) state<=S2; else  state<=S1;  
	         S2:     if(bits == 0) state<=S3; else  state<=S1;
           S3:     if(bits == 0) state<=S0; else  state<=S4;
           S4:     if(bits == 0) state<=S0; else  state<=S1;
           default:              state<=S0;
       endcase
    end
    
	always @(posedge clk or negedge reset_n)//同步时序逻辑描述输出条件
	if(!reset_n)
	   Det_Done<=0;
	else begin
	   case(state)
	         S0:     Det_Done <= 1'b0;
	         S1:     Det_Done <= 1'b0;  
	         S2:     Det_Done <= 1'b0;
           S3:     Det_Done <= 1'b0;
           S4:     if(bits == 0) Det_Done<=1'b1; else  Det_Done<=1'b0;
           default:Det_Done <= 1'b0;
       endcase
    end
endmodule
 

异步复位,同步释放

一般来说,同步系统,都使用异步复位。这是因为同步复位比异步复位的电路实现要浪费更多电路资源。
notion image
module rstsync(
	  input  clk,
	  input  rst_async_n,//异步复位信号
	  output rst_sync_n  //同步释放信号
);
		reg rst_s1;
		reg rst_s2;

		always@(posedge clk or negedge rst_async_n)begin
			 if(!rst_async_n)begin
				   rst_s1 <= 1'b0;
				   rst_s2 <= 1'b0;
			 end
			 else begin
				   rst_s1 <= 1'b1;
				   rst_s2 <= rst_s1;
			 end
			  
		end
			
			assign rst_sync_n = rst_s2;

endmodule
 

帧头检测

notion image

边沿检测

用verilog实现1bit信号边沿检测功能,输出一个周期宽度的脉冲信号
重点在上升沿,下降沿,上升沿或下降沿
定义了一个2位的寄存器变量data_r,data_r[0]用来存储当前的状态,data_r[1]用来存储上一拍的状态
每个时钟到来的时候,向左移位,相对于当前时钟的上上拍信号舍弃掉,只保留相对于当前时钟上一拍的状态,存储在data_r的高位上,即data_r[1]表示的是上一拍的状态
module EdgeDetect(
		 input clk,
		 input rst_n,
		 input data,
		 output pos_edge,
		 output neg_edge,
		 output data_edge
		);
		
		reg [1:0] data_r;

		always@(posedge clk or negedge rst_n)begin
				 if(rst_n == 1'b0)begin
						  data_r <= 2'b00;
				 end
				 else begin
						  data_r <= {data_r[0],data};//拼接出来高位为上一拍,低位为当前拍
				 end
		end
		
		assign pos_edge  =  ~data_r[1] &  data_r[0];          //01
		assign neg_edge  =  data_r[1]  & ~data_r[0];          //10
		assign data_edge =  pos_edge | neg_edge;
//上升沿之前是0,现在为1,所以上个周期传输的signal[1]是0,应该取反。
endmodule

加法器

描述带进位输入、输出的两位全加器
先写一位全加器,然后再写个顶层模块,调用两个一位全加器,其中第一个加法器的cout作为第二个加法器的cin
//公式
S    = a ^ b ^ cin;
Cout = a & b | a & cin | b & cin;
//一位全加器
module fulladder(
 input  a,
 input  b,
 input  cin,
 output s,
 output cout
);
 assign s    = a ^ b ^ cin;
 assign cout = a & b | a & cin | b & cin;
endmodule


//2位全加器
module top(a,b,cin,s,cout);

 input [1:0]  a;
 input [1:0]  b;
 input        cin;
 output [1:0] s;
 output       cout;
 wire         carry;
 //采用结构化描述的方式实现一个2位加法器
 fulladder m0(a[0],b[0],cin,  s[0],carry);
 fulladder m1(a[1],b[1],carry,s[1],cout );
endmodule

串并转换

verilog实现8位串行数据转并行数据
解串器(deserialize):串行信号转并行信号
输入信号有:时钟信号clk,复位信号rst,串行数据输入信号din。
输出信号为8bit并行信号dout。
每经过8个时钟周期,便把收到的8个串行信号合成并行信号并输出,等下8个时钟周期过后再输出下一个并行信号。
module deserialize(
		 input rst,clk,din,
		 output reg [7:0] dout
		);
		
		reg [7:0] data;
		reg [2:0] cnt;
		
		always@(posedge clk or negedge rst)begin
					 if(rst == 1'b0)begin
							  data <= 8'b0;
							  cnt  <= 3'b0;
							  dout <= 8'b0;
					 end
					 else begin
							  if(cnt == 3'b111) begin //get all 8bit data,change dout
									   dout[7-cnt] <= din;//将串行信号输入dout
									   dout[7:1]   <= data[7:1];
									   cnt         <= 3'b0;
								end
							  else begin//没有加满时,持续输入din总共7个数字,0—6
										  data[7-cnt] <= din;
										  cnt <= cnt+1;
							  end
					 end
		end
endmodule
`timescale 1ns/1ns
 
module deseralize_tb;
 
reg RST,CLK,DIN;
wire[7:0] DOUT;
integer i,j;
 
deserialize U_deserialize(.rst(RST),.clk(CLK),.din(DIN),.dout(DOUT));
 
initial 
begin
	RST = 1;
	#5 RST = 0;
	#2 RST = 1;
end
 
initial 
begin
	CLK = 0;
	for(i = 0;i<100000;i = i+1)
	begin
		#2 CLK = ~CLK;
	end
end
 
initial
begin
	#1 DIN = 0;
	for(j = 0;j<10000;j = j+1)
	begin//只有7位输入,可以造成相邻的dout不同
		#4 DIN = 0;
		#4 DIN = 1;
		#4 DIN = 1;
		#4 DIN = 0;
		#4 DIN = 1;
		#4 DIN = 0;
		#4 DIN = 1;
	end
end
 
endmodule
notion image

跨时钟域

单bit(慢时钟域到快时钟域):
用快时钟打两拍,直接采一拍大概率也是没问题的,两拍的主要目的是消除亚稳态
慢时钟域信号到快时钟域信号,一般情况下都是打两拍(单比特传输)也叫两级寄存器同步
module clockdomain(
		 input clk,
		 input rst_n,
		 input signal_in,
		 output signal_out
		);
		
		reg [1:0] signal_r;
		
		always@(posedge clk or negedge rst_n)begin
				 if(!rst_n)begin
					  signal_r <= 2'b00;
				 end
				 else begin
					  signal_r <= {signal_r[0],signal_in};//高位为之前状态,低位为当前输入状态
				 end
		end
		assign signal_out = signal_r[1];//输出为之前状态,第二拍
endmodule
单bit(快时钟域到慢时钟域):
握手、异步FIFO、异步双口RAM;
快时钟域的信号脉宽较窄,慢时钟域不一定能采到,可以通过握手机制让窄脉冲展宽,慢时钟域采集到信号后再“告诉”快时钟域已经采集到信号,确保能采集到;
快时钟域到慢时钟域(clka慢时钟域,clkb快时钟域)
  • 将快的时钟域中的信号进行展宽
  • 对展宽的信号在慢的时钟域2级同步
  • 在慢时钟域中产生反馈信号来对快的展宽信号进行拉低处理
跨时钟域处理从快时钟域到慢时钟域,如果是下面第一个图,cklb则可以采样到signal_a_in,但是如果 只有单脉冲,如第二个图,则不能确保采样掉signal_a_in。这个时候用两级触发器同步是没有用的。
notion image
module Sync_Pulse(
		  input      clka,
		  input      clkb,
		  input      rst_n,
		  input      pulse_ina,
		  output     pulse_outb,
		  output     signal_outb
		);
		//-------------------------------------------------------
			reg       signal_a;
			reg       signal_b;
			reg   [1:0]  signal_b_r;
			reg   [1:0]  signal_a_r;
		//-------------------------------------------------------
		//在clka下,生成展宽信号signal_a
		always @(posedge clka or negedge rst_n)begin
				  if(rst_n == 1'b0)begin
				    signal_a <= 1'b0;
				  end
				  else if(pulse_ina == 1'b1)begin
				    signal_a <= 1'b1;
				  end
				  else if(signal_a_r[1] == 1'b1)
				    signal_a <= 1'b0;
				  else
				   signal_a <= signal_a;
		end
		//-------------------------------------------------------
		//在clkb下同步signal_a
		always @(posedge clkb or negedge rst_n)begin
					  if(rst_n == 1'b0)begin
					    signal_b <= 1'b0;
					  end
					  else begin
					    signal_b <= signal_a;
					  end
		end
		//-------------------------------------------------------
		//在clkb下生成脉冲信号和输出信号
		always @(posedge clkb or negedge rst_n)begin
			  if(rst_n == 1'b0)begin
			   signal_b_r <= 2'b00;
			 end
			 else begin
			   signal_b_r <= {signal_b_r[0], signal_b};
			  end
		end
		assign   pulse_outb = ~signal_b_r[1] & signal_b_r[0];
		assign   signal_outb = signal_b_r[1];
		//-------------------------------------------------------
		//在clka下采集signal_b[1],生成signal_a_r[1]用于反馈拉低signal_a
		always @(posedge clka or negedge rst_n)begin
			  if(rst_n == 1'b0)begin
			   signal_a_r <= 2'b00;
			  end
			 else begin
			   signal_a_r <= {signal_a_r[0], signal_b_r[1]};
			  end
		end
endmodule
多bit:
异步FIFO、异步双口RAM、握手、格雷码;

同步FIFO

深度16,位宽8
module Syn_fifo
#(
  parameter  DATA_WIDTH  =  8,
  parameter  ADDR_WIDTH  =  4,
  parameter  RAM_DEPTH  =  (1 << ADDR_WIDTH)
)
(
  input      clk,
  input      rst_n,
  input  [DATA_WIDTH-1:0]   data_in,
  input      wr_en,
  input      rd_en,
  output reg  [DATA_WIDTH-1:0]   data_out,
  output      empty,      //fifo empty
  output      full       //fifo full
);
reg   [ADDR_WIDTH-1:0]   wr_cnt;
reg   [ADDR_WIDTH-1:0]   rd_cnt;
reg   [ADDR_WIDTH-1:0]   status_cnt;
reg   [DATA_WIDTH-1:0]   data_ram;
//-------------------------------------------------------
assign  full = (status_cnt == (RAM_DEPTH-1))? 1'b1: 1'b0;
assign  empty = (status_cnt == 0)? 1'b1: 1'b0;
//Syn
reg   rd_en_r;
always @(posedge clk or negedge rst_n)begin
  if(rst_n == 1'b0)begin
    rd_en_r <= 0;
  end
  else begin
    rd_en_r <= rd_en;
  end
end
//-------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
  if(rst_n == 1'b0)begin
    wr_cnt <= 0;
  end
  else if(wr_cnt == RAM_DEPTH-1)
    wr_cnt <= 0;
  else if(wr_en)begin
    wr_cnt <= wr_cnt + 1'b1;
  end
  else
    wr_cnt <= wr_cnt;
end
always @(posedge clk or negedge rst_n)begin
  if(rst_n == 1'b0)begin
    rd_cnt <= 0;
  end
  else if(rd_cnt == RAM_DEPTH-1)
    rd_cnt <= 0;
  else if(rd_en)begin
    rd_cnt <= rd_cnt + 1'b1;
  end
  else
    rd_cnt <= rd_cnt;
end
always @(posedge clk or negedge rst_n)begin
  if(rst_n == 1'b0)begin
    data_out <= 0;
  end
  else if(rd_en_r)begin
    data_out <= data_ram;
  end
end
always @(posedge clk or negedge rst_n)begin
  if(rst_n == 1'b0)begin
    status_cnt <= 0;
  end
  else if(rd_en && !wr_en && (status_cnt != 0))begin
    status_cnt <= status_cnt - 1;
  end
  else if(wr_en && !rd_en && (status_cnt != RAM_DEPTH-1))
    status_cnt <= status_cnt + 1;
  else
    status_cnt <= status_cnt;
end
//-------------------------------------------------------
//Syn_Dual_Port_RAM
integer   i;
reg   [DATA_WIDTH-1:0]  register[RAM_DEPTH-1:0];
always @(posedge clk or negedge rst_n)begin
  if(rst_n == 1'b0)begin
    for(i = 0; i < RAM_DEPTH; i = i + 1)
      register[i] <= 0;
  end
  else if(wr_en == 1'b1)
    register[wr_cnt] <= data_in; 
end
always @(posedge clk or negedge rst_n)begin
  if(rst_n == 1'b0)begin
    data_ram <= 0;
  end
  else if(rd_en == 1'b1)
    data_ram <= register[rd_cnt];
  else
    data_ram <= data_ram;
end
endmodule

格雷码转换

二进制转换为格雷码
原数右移一位再与原数异或
module bin_to_gray(
 bin_in,
 gray_out
);

parameter data_width = 4;
input  [data_width-1:0] bin_in;
output [data_width-1:0] gray_out;

assign gray_out = (bin_in >> 1)^bin_in;

endmodule
格雷码转换为二进制
二进制最高位取格雷码最高位,然后循环做二进制高位与低一位的格雷码异或
module gray_to_bin(
 gray_in,
 bin_out
);

parameter data_width = 4;
input  [data_width-1:0] gray_in;
output [data_width-1:0] bin_out;
reg    [data_width-1:0] bin_out;

always@(gray_in)begin
 bin_out[3] = gray_in[3];
 bin_out[2] = gray_in[2]^gray_in[3];
 bin_out[1] = gray_in[1]^gray_in[2];
 bin_out[0] = gray_in[0]^gray_in[1];
end

endmodule
 
 
 

参考资料