中科大OJ Verilog 在线评测题解 53-61

近跟着老师学习Verilog,做了中科大的练习题,将答案记录一下

Q53 查找表

题目描述

在本题中,请设计一个 8x1 存储器的电路,其中写入内存是通过移位来完成的,而读取是“随机访问”,就像在典型的 RAM 中一样,以三个输入(A、B、C)作为地址,实现对存储器单元的随机访问。
首先,使用 8 个 D 型触发器实现一个 8 位移位寄存器。将触发器输出标记为 Q[0]…Q[7]。移位寄存器输入应被调用 S,它将输入 Q[0](高位通过移位操作输入)。通过使能信号控制是否移动。电路的行为应如下:当 ABC 为 000 时,Z=Q[0],ABC 为 001 时,Z=Q[1],等等。您的电路应仅包含 8 位移位寄存器和多路复用器(该电路称为 3 输入查找表(LUT))。

输入格式

1bit信号S、使能信号以及多路选择器控制信号ABC

输出格式

在使能信号为true的情况下,每个时钟上升沿移位寄存器进行移位操作,其中Q[0]的值从S处获得。根据ABC选择信号的值,输出信号Z从Q[0]...Q[7]中取值。

代码

module top_module(
  input clk,
  input enable,
  input S,
  input A, B, C,
  output reg Z 
);
  // Write your code here
    reg [7:0] q;


  always@(posedge clk) begin
      if (enable) begin
          q <= {q[6:0], S};  //S在D0触发器?实现左移功能
      end
    end

    always@(*) begin

        Z = q[{A,B,C}];   //代替case等同q[0]-q[7]

    end
endmodule

Q54 ROM

题目描述

在数字电路中,不可避免的需要对数据进行存储和访问,虽然寄存器能够实现数据的存储功能,但使用寄存器进行大量数据的存储并不现实,这是因为:第一,寄存器实现成本过高;第二,寄存器结构复杂,难以提高集成度。因此,一般使用存储器进行大段数据的存储,常见的存储器有ROM(只读存储器)、RAM(随机访问存储器)和FIFO(先进先出队列)。ROM顾名思义,该存储器里面的数据是预选设置好的,在使用时只能读取,不能写入,在使用FPGA作为硬件平台时,一般是通过EDA工具中的IP生成工具来生成的。此外,我们也可以通过使用Verilog定义一个数组来实现,并由EDA工具自动例化成相应的IP核,这样做对于开发者来说简单了很多,但性能多少会受些影响。
对于存储器,其内部数据的初始化有两种方式:(1)在Verilog代码中指定初始内容(2)在外部文件中指定初始内容。在实际中,一般采用第二种方式。
对于存储器来说,有两个非常重要的参数:地址位宽和数据位宽,地址位宽与存储器的存储深度(即存储单元数量)相关,如地址宽度为N,则存储器深度为2N,数据位宽则表示每个存储单元所包含的bit数。
下例是一个4*8bit的ROM实例,该ROM通过在Verilog代码中指定内容进行初始化
module rom_4x8bit(
input      [1:0] addr
output   [7:0] q);
reg  [7:0] mem [3:0];
initial
begin
mem[0] = 8’h00;
mem[1] = 8’h00;
mem[2] = 8’h00;
mem[3] = 8’h00;
end
assign q = mem[addr];
endmodule
试根据上述示例,设计一个8*4bit的ROM,并对其进行初始化,使其初始化数据为“0,1,2,3,…”

输入格式

3'b000

输出格式

4'd0

代码

module top_module(
	input	[2:0] addr,
	output	[3:0]	q
);

    reg  [3:0]mem[7:0];

    initial  begin

        mem[0] = 4'd0;
        mem[1] = 4'd1;
        mem[2] = 4'd2;
        mem[3] = 4'd3;
        mem[4] = 4'd4;
        mem[5] = 4'd5;
        mem[6] = 4'd6;
        mem[7] = 4'd7;

    end

    assign q = mem[addr];

endmodule

Q55 RAM

题目描述

RAM全称为随机访问存储器,可以对其内部的单元进行读写访问,根据读写端口数量和类型,可以分为单端口(读写端口共用地址信号)、简单双端口(读写端口独立,可同时进行读写操作)、全双端口(有两套读写端口)等不同类型。
下图是三种不同RAM的接口信号对比,图中列出的都是基本接口信号,根据具体要求,还可以增加复位、读使能、字节使能等端口信号。

下例是一个4*8bit的单端口RAM的实例,采用外部文件进行初始化

module ram_one_port(
input 	clk,
input	[1:0] addr,
input	wr_en,
input	[7:0] wr_data,
output	[7:0] rd_data);
reg	[7:0] mem[3:0];
initial
begin
$readmemh("memfile.dat",mem);
end
assign rd_data = mem[addr];
always@(posedge clk)
begin
    if(wr_en)
	mem[addr] <= wr_data;
end
endmodule

上例中使用系统函数$readmemh,该函数可以将文件中的数据初始化到存储器数组中,memfile.dat为RAM的初始化文件,内容如下(16进制格式,可以使用文本工具编辑):
00
01
02
03
试根据上述示例,设计一个8*16bit的简单双端口RAM,并使用memfile.dat文件对其初始化,在本例中,该文件已在后台提供,用户可直接使用。
代码模板:
module ram_one_port(
input clk,
input wr_en,
input [2:0] wr_addr,
input [15:0] wr_data,
input [2:0] rd_addr,
output [15:0] rd_data);

endmodule

输入格式

时钟信号clk 使能信号wr_en 3位宽写地址wr_addr 8位宽写数据wr_data 3位宽读地址rd_addr

输出格式

8位宽读数据rd_data

示例波形

代码

module ram_one_port(
input 		  clk,
input		  wr_en,
input 	[2:0]  wr_addr,
input	[15:0] wr_data,
input	[2:0]  rd_addr,
output	[15:0] rd_data);
    reg	[15:0] mem[7:0];
initial
begin
$readmemh("memfile.dat",mem);
end
assign rd_data = mem[rd_addr];
always@(posedge clk)
begin
    if(wr_en)
	mem[wr_addr] <= wr_data;
end
endmodule

Q56 有限状态机

题目描述

下图是摩尔型有限状态机的结构图,我们可以发现其包含三个部分,第一部分为纯组合逻辑,通过现态和输入信号生成次态信号,第二部分为时序逻辑,该时序逻辑非常简单,只包含一个带有复位功能的寄存器单元,复位时现态信号变为初始值,否则在每个时钟的上升沿将次态信号赋值给现态信号。第三部分为组合逻辑,该部分通过现态信号生成各输出信号。

对于上述电路,其Verilog代码实现为:
module traffic_ctrl(
input   clk,
input   rst,
input   timer_pulse,
output  green_light
);
parameter   C_PASS  = 2'b00;
parameter   C_TRANS = 2'b01;
parameter   C_STOP  = 2'b10;
reg [1:0]   curr_state;
reg [1:0]   next_state;
//有限状态机第一部分
always@(*)
begin
    if(timer_pulse)
    begin
        case(curr_state)
            C_PASS: next_state = C_TRANS;
            C_TRANS:next_state = C_STOP;
            C_STOP: next_state = C_PASS;
            default:next_state = C_PASS;
        endcase
    end
    else
        next_state = curr_state;
end
//有限状态机第二部分
always@(posedge clk or posedge rst)
begin
    if(rst)
        curr_state  <= C_PASS;
    else
        curr_state  <= next_state;
end
//有限状态机第三部分,各输出信号的赋值都应放在此部分
assign green_light = (curr_state==C_PASS)? 1'b1 : 1'b0;
//...
endmodule
试用Verilog代码实现下图中的摩尔型状态机,有两个状态,一个输入(in),一个输出(out)。实现这个状态机。请注意,电路采用异步复位,复位状态是B。

输入格式

clk areset: Asynchronous reset to state B 1bit in

输出格式

1bit out

代码

module top_module(
    input clk,
    input areset,    // Asynchronous reset to state B
    input in,
    output out);//  
  
    parameter A=0, B=1; 
	reg state, next_state;
    always @(*) begin    //有限状态机第一段
        // State transition logic
        if(~in)
    begin
        case(state)
            A: next_state = B;
            B:next_state = A;
            default:next_state = B;
        endcase
    end
    else
        next_state = state;
	end
	always @(posedge clk, posedge areset) begin    //有限状态机第二段
        // State flip-flops with asynchronous reset
    if(areset)
        state  <= B;
    else
        state  <= next_state;
    end
    //有限状态机第三段,信号输出逻辑
    assign out = (state == B);
endmodule

Q57 读代码找错误

题目描述

如果设计的Verilog模块不能正常工作,说明存在语法或者功能上的错误,这时需要对错误进行定位,对于语法错误,可通过EDA工具中的语法检查功能进行定位,对于已经掌握了Verilog语法的用户来说,很容易便能解决。对于电路功能上的错误,可以通过以下两种途径定位:阅读代码、电路仿真。
以下是一个8bit位宽的二选一选择器的Verilog代码,但存在错误,请仔细阅读代码,修改代码使其能正常工作。

输入格式

对下面的代码进行改错即可

输出格式

对下面的代码进行改错即可 注:有两行代码有问题 以及,sel取何值时选择哪一个,代码里已经有暗示。

代码

module top_module (
    input sel,
    input [7:0] a,
    input [7:0] b,
    output [7:0]out  );//这就是错误2,差点儿忘了这个也需要检查

    assign out = ({8{~sel}} & a) | ({8{sel}} & b);//操作位数需要保持一致

endmodule

Q58 编写仿真文件

题目描述

在Verilog设计中,仿真是非常重要的一环,绝大部分的电路功能错误都可以通过仿真进行定位并解决。
仿真的过程为:用户使用Verilog编写一个没有任何输入信号的测试模块(一般命名为tb.v,即testbench的缩写),在该模块中调用被仿真的模块(及我们使用Verilog编写的功能模块),并使用initial等关键字构造被测模块的输入信号,通过专门的仿真工具进行仿真,并查看各个信号的波形变化

以四选一选择器的仿真为例,被测模块和测试文件的Verilog代码如下所示:

仿真结果如下图所示:

通过观察波形我们可以发现,该电路的仿真波形符合四选一选择器的行为特性,Verilog代码设计正确。
在initial进程块内,语句都是顺序执行的,通过延时符号“#”实现时序控制。例如:
`timescale 1ns/1ps
module tb();
reg a;
initial begin
a = 1’b0;
#10 a = 1’b1;
#20 a = 1’b0;
#20 $finish;
end
对应的波形为:

其中`timescale 1ns/1ps表示时间精度,分辨率为1ps,时间单位为1ns,#10表示延时10个时间单位(即10ns),$finish为仿真专用的系统函数,表示仿真结束
试编写测试文件,实现如下波形:

输入格式

输出格式

代码

module tb();
reg a,b;
initial begin
a = 1'b1;
b = 1'b0;
#10 b = 1'b1;
#10 a = 1'b0;
#10 b = 1'b0;
#10 a = 1'b1;    
#10 $finish;
end
endmodule

Q59 组合逻辑模块仿真

题目描述

以下是给定模块mymodule的Verilog代码:

module mymodule(
input a,b,
output q);
assign q = a & b;
endmodule

试编写仿真文件,使其符合如下示例波形(板载时钟为100MHz,虚线分隔时间每格为10ns)

输入格式

输出格式

代码 提示:应该是这题的上传者在他的root/testbench/tb/写死了10ns

module tb();
reg a,b;
wire q;
  
//对ab信号进行初始化
initial begin
a=0;
b=0;
#3 b=1;
#2 b=0; a=1;
#2 b=1;
#2 b=0; a=0;
#2 b=1;
#2 b=0; a=1;
#2 b=1;
#2 b=0; a=0;
#2 $finish;

end
//例化mymodule模块
mymodule test(
.a(a),
.b(b),
.q(q)
);

endmodule  



module mymodule(
input a,b,
output q
);
  
assign q = a & b;
  
endmodule

Q60 生成时钟信号

题目描述

给定被测试模块dut,其verilog代码如下:

module dut(input clk, output reg [2:0]out);
  //测试模块
  always @(posedge clk)
    out <= out + 1'b1;  
endmodule

请编写仿真文件,对该模块进行仿真(dut模块可直接调用,不需要用户编写),clk信号应符合以下波形:
Hint:
应注意clk的周期长度(如上图)

输入格式

输出格式

代码

module tb();
wire [2:0]out;//必要输出信号 	
	//信号定义
	reg clk;
    //信号生成
    initial clk = 0;
    always #5 clk = ~clk;
    //模块例化
    
    dut dut_inst(
        .clk(clk),
        .out(out)
    );
endmodule

module dut(input clk, output reg [2:0]out);
  //测试模块
  always @(posedge clk)
    out <= out + 1'b1;  
endmodule

Q61 单端口 RAM 仿真

题目描述

以下是一个单端口RAM的verilog描述:

module ram_one_port(
    input                 clk,
    input        [1:0]    addr,
    input                 wr_en,
    input	[7:0]    wr_data,
    output	[7:0]    rd_data);
    reg		[7:0]    mem[3:0];
    initial
    begin
        $readmemh("memfile.dat",mem);
    end
    assign rd_data = mem[addr];
    always@(posedge clk)
    begin
	if(wr_en)
	    mem[addr] <= wr_data;
    end
endmodule

如对该模块进行仿真,需要在tb文件中生成clk、addr、wr_en、wr_data等RAM所需的输入信号(在tb中都需定义为reg类型),并查看rd_data端口的输出数据。
clk信号可以使用以下语句生成:

initial begin
    clk = 0;
    forever #5 clk = ~clk; //生成周期为10的一个时钟信号,forever为verilog的关键字
end

addr信号应该在时钟的上升沿变化,我们可以借助前面生成的clk信号来生成addr,如:

initial begin
    addr = 2'b0;
    repeat(4) begin 		//repeat为verilog关键字,表示重复操作
	@(posedge clk);		//等待clk信号的上升沿到来
	#1 addr = addr + 1;	//clk上升沿1个时间单位后,addr加一
    end
end

对于wr_en信号,我们可以使其持续4个周期的高电平,如下所示:

initial begin
	wr_en = 0;
	#501;			//延时一段时间,
	@(posedge clk);
	#1 wr_en = 1;
	@(posedge clk);
	@(posedge clk);
	@(posedge clk);
	@(posedge clk);	//等待4个clk上升沿
	#1 wr_en = 0;
end

此外,我们还可以使用系统函数生成随机数,来作为测试数据,以wr_data为例:

initial begin
    wr_data = 8'h0;
    repeat(4) begin
    	wait(wr_en==1'b1);
	#1 wr_data = $random%256;
    	@(posedge clk);
    end
end

请根据上述提示,按照以下波形的要求,编写单端口ram的仿真文件:

注意:由于模块内包含随机数的特殊性,OJ系统不会自动判断本题对错,只会根据提交代码生成仿真波形,具体检查方式待定。示例波形为运行一次参考代码所得结果,仅供参考。

输入格式

无输入,所有所需信号已在模块内预定义

输出格式

无输出,评测系统会抓取模块内 clk, addr, wr_en, wr_data, rd_data 显示。

代码

module ram_one_port(
	input 	clk,
	input	[1:0] addr,
	input	wr_en,
	input	[7:0] wr_data,
	output	[7:0] rd_data
);
	reg		[7:0] mem[3:0];
	initial
	begin
      mem[0] = 8'b0;
      mem[1] = 8'b0;
      mem[2] = 8'b0;
      mem[3] = 8'b0;
	end
	assign rd_data = mem[addr];
	always@(posedge clk)
	begin
		if(wr_en)
			mem[addr] <= wr_data;
	end
endmodule


module tb(
);
	//信号定义
	reg				clk,wr_en;
	reg		[1:0] 	addr;
	reg		[7:0] 	wr_data;
	wire	[7:0] 	rd_data;
	//信号生成
    initial begin
    clk = 0;
    forever #5 clk = ~clk; //生成周期为10的一个时钟信号,forever为verilog的关键字
end
    initial begin
    addr = 2'b0;
    repeat(4) begin 		//repeat为verilog关键字,表示重复操作
	@(posedge clk);		//等待clk信号的上升沿到来
	#1 addr = addr + 1;	//clk上升沿1个时间单位后,addr加一
    end
end
    initial begin
	wr_en = 0;
	#501;			//延时一段时间,
	@(posedge clk);
	#1 wr_en = 1;
	@(posedge clk);
	@(posedge clk);
	@(posedge clk);
	@(posedge clk);	//等待4个clk上升沿
	#1 wr_en = 0;
end
    initial begin
    wr_data = 8'h0;
    repeat(4) begin
    	wait(wr_en==1'b1);
	#1 wr_data = $random%256;
    	@(posedge clk);
    end
end
	//例化被测模块
ram_one_port ram_one_port(
    .clk(clk),
    .addr(addr),
    .wr_en(wr_en),
    .wr_data(wr_data),
    .rd_data(rd_data)
);    
endmodule

  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
这是一道经典的位运算题目,考察对二进制的理解和位运算的熟练程度。 题目描述: 给定一个长度为 $n$ 的数组 $a$,初始时每个数的值都为 $0$。现在有 $m$ 个操作,每个操作为一次询问或修改。 对于询问,给出两个整数 $l,r$,求 $a_l \oplus a_{l+1} \oplus \cdots \oplus a_r$ 的值。 对于修改,给出一个整数 $x$,表示将 $a_x$ 的值加 $1$。 输入格式: 第一行两个整数 $n,m$。 接下来 $m$ 行,每行描述一次操作,格式如下: 1 l r:表示询问区间 $[l,r]$ 的异或和。 2 x:表示将 $a_x$ 的值加 $1$。 输出格式: 对于每个询问操作,输出一个整数表示答案,每个答案占一行。 数据范围: $1 \leq n,m \leq 10^5$,$0 \leq a_i \leq 2^{30}$,$1 \leq l \leq r \leq n$,$1 \leq x \leq n$ 输入样例: 5 5 2 1 2 3 1 2 4 2 2 1 1 5 输出样例: 0 2 解题思路: 对于询问操作,可以利用异或的性质,即 $a \oplus b \oplus a = b$,将 $a_l \oplus a_{l+1} \oplus \cdots \oplus a_r$ 转化为 $(a_1 \oplus \cdots \oplus a_{l-1}) \oplus (a_1 \oplus \cdots \oplus a_r)$,因为两个前缀异或后的结果可以相互抵消,最后的结果即为 $a_1 \oplus \cdots \oplus a_{l-1} \oplus a_1 \oplus \cdots \oplus a_r = a_l \oplus \cdots \oplus a_r$。 对于修改操作,可以将 $a_x$ 对应的二进制数的每一位都分离出来,然后对应位置进行修改即可。由于只有加 $1$ 操作,所以只需将最后一位加 $1$ 即可,其余位不变。 参考代码:

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值