前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >题解 | Verilog刷题解析及对应笔试面试注意点【6-9】(涉及==和===、for展开问题等)

题解 | Verilog刷题解析及对应笔试面试注意点【6-9】(涉及==和===、for展开问题等)

作者头像
FPGA探索者
发布2022-05-26 15:41:28
9950
发布2022-05-26 15:41:28
举报
文章被收录于专栏:FPGA探索者

在B站【FPGA探索者】录制了试题讲解视频,本文更新了第6-9题文字解析

目的:不仅仅是解题,更多的是想从真实的FPGA和数字IC实习秋招和实际工程应用角度,解读一些【笔试面试】所注意的知识点,做了一些扩展

第六题——多功能数据处理(有符号数case语句,简单)

第七题——求两个数的差值(if...else,==和===的区别,有2个笔试注意点,建议看一看)

第八题——使用generate…for语句简化代码(for和generate...for用法、区别,给出两种实现方式)

第九题——子模块例化(时序逻辑和组合逻辑两种实现方式问题集中在时序逻辑时使用2个子模块造成答案错误,需要分析下信号的波形,可以多体会一下;给出两种实现方式)

第六题——多功能数据处理(有符号数、case语句,简单不过多讲)

有符号数和case可以参考:

题解 | Verilog刷题解析及对应笔试面试注意点【1-5】(涉及复位、有符号数问题等)

1. 题目

根据指示信号select的不同,对输入信号a,b实现不同的运算。输入信号a、b为8bit有符号数,

当select[1:0] = 0,输出a;

当select[1:0] = 1,输出b;

当select[1:0] = 2,输出a+b;

当select[1:0] = 3,输出a-b。

代码语言:javascript
复制
`timescale 1ns/1ns
module data_select(
  input clk,
  input rst_n,
  input signed[7:0]a,
  input signed[7:0]b,
  input [1:0]select,
  output reg signed [8:0]c
);
endmodule

2. 解析

有符号数,输入8-bit,输出9-bit,最好是手动扩展符号位再输出,加、减操作时也是手动扩展符号位再加减。

本题目比较简单,但是还是有一些值得注意的点,中间改动一些东西可能就会出错。因为输入输出都已经直接定义了signed有符号数类型,所以直接相加、相减也没有问题,不会出现运算错误。

有符号数 + 有符号数 = 有符号数

这其中,如果加数中有无符号数,那么就会按照无符号运算。

如果表达式中有一个无符号数,则所有的操作数都会被强行转换为无符号数;

即:有符号A + 无符号B时,会将补码表示的有符号A当成无符号数A1,,再计算A1+B,这样得到的结果就是错的了。

两种解决方法:

(1)涉及到有符号数运算时,和有符号相关的输入、输出、中间变量均定义成signed有符号数,这样全部遵循有符号数运算规则;

(2)用位拼接符补齐符号位;

3. 代码

代码语言:javascript
复制
// Verilog 使用位拼接,扩展符号位
// 作者:FPGA探索者
`timescale 1ns/1ns
module data_select(
    input clk,
    input rst_n,
    input signed[7:0]a,
    input signed[7:0]b,
    input [1:0]select,
    output reg signed [8:0]c
);  

always @ (posedge clk or negedge rst_n)
begin
    if( ~rst_n ) begin
        c <= 9'b0;
    end
    else begin
        case ( select )
            2'b00 : begin
                c <= {a[7], a};
            end
            2'b01 : begin
                c <= {b[7], b};
            end
            2'b10 : begin
                c <= {a[7], a} + {b[7], b};
            end
            2'b11 : begin
                c <= {a[7], a} - {b[7], b};
            end
            default : begin
                c <= 9'b0;
            end
        endcase
    end
end    

endmodule

第七题——求两个数的差值(if...else,有个笔试注意点,建议看一看)

本题目虽然简单,但是值得探究if...else仿真时的注意点,笔试遇到过,你不一定能答对哦~

1. 题目

根据输入信号a,b的大小关系,求解两个数的差值:输入信号a、b为8bit位宽的无符号数。

如果a>b,则输出a-b;

如果a≤b,则输出b-a。

代码语言:javascript
复制
`timescale 1ns/1ns
module data_minus(
  input clk,
  input rst_n,
  input [7:0]a,
  input [7:0]b,

  output  reg [8:0]c
);
endmodule

2. 解析

if..else语句即可。关注点还是和第6题一样,有符号数问题。

2.1 if...else的注意要点

if...else的注意点:

if里只能判断条件是0还是1,如果出现X或者Z,则判断不出来。

答案:不一样!

(1)

对于

always @ (*)

begin

if(a == 1'b0)

b = 1'b0;

else

b = 1'b1;

end

当输入a是0时,b输出0,否则b输出1(否则的意思是只要不等于0!包括了x和z也是不等于0,不是只有1不等于0);

其他两种情况夏是一样的,if里面只能判断0和1,当出现x和z时到else里。

具体仿真图如下:

2.2 等号运算符==和===的区别

有人要问了,如果直接判断if(a == 1’bx)或者if(a == 1’bz)呢?

当a = 1’bx时,if里的a==1’bx结果不为真!

这一部分又涉及到了两个等号==和三个等号===。

参考夏宇闻老师的书籍:

if(A==1’bx) $display(“AisX”); (当A等于X时,这个语句不执行)

if(A===1’bx) $display(“AisX”); (当A等于X时,这个语句执行)

注意,这部分在很多笔试题中出现过,判断条件和对应输出。

3. 代码

代码语言:javascript
复制
`timescale 1ns/1ns
module data_minus(
  input clk,
  input rst_n,
  input [7:0]a,
  input [7:0]b,

  output  reg [8:0]c
);

always @ (posedge clk or negedge rst_n)
begin
    if( ~rst_n ) begin
        c <= 8'b0;
    end 
    else begin
        if( a > b ) begin
            c <= a - b;
        end 
        else begin
            c <= b - a;
        end 
    end 
end    

endmodule

第八题——使用generate…for语句简化代码(for和generate...for用法、区别)

1. 题目

在某个module中包含了很多相似的连续赋值语句,请使用generata…for语句编写代码,替代该语句,要求不能改变原module的功能。使用Verilog HDL实现以上功能并编写testbench验证。

代码语言:javascript
复制
module template_module( 
    input [7:0] data_in,
    output [7:0] data_out
);
    assign data_out [0] = data_in [7];
    assign data_out [1] = data_in [6];
    assign data_out [2] = data_in [5];
    assign data_out [3] = data_in [4];
    assign data_out [4] = data_in [3];
    assign data_out [5] = data_in [2];
    assign data_out [6] = data_in [1];
    assign data_out [7] = data_in [0];
    
endmodule

2. 解析

2.1 for循环

for循环,必须在always块里使用。对应的,always块内的变量要声明成reg类型。

for(表达式1;表达式2;表达式3),执行时对表达式1、2、3和C语言中一样:

(1)执行表达式1,一般是循环变量赋初值;

(2)执行表达式2,若结果为真则执行for里面的内容,否则结束for语句;

(3)执行完for里面的语句,执行表达式3,一般是循环变量自增、自减、移位等操作,回到(2);

verilog的for和C语言的for的不同点;

C语言的for里面的语句是串行顺序执行,而verilog的for内的语句实际是并行的,只是为了写代码方便才用for对多个同样的结构赋值。

比如:

实现移位寄存器时:

代码语言:javascript
复制
integer i;
always @ (posedge clk)
begin
    data_reg[0] <= data_in;
    for(i = 0; i < 4; i = i+1) begin
        data_reg[i+1] <= data_reg[i];
    end
end

等效的语句:

代码语言:javascript
复制
always @ (posedge clk)
begin
    data_reg[0] <= data_in;
    data_reg[1] <= data_reg[0];
    data_reg[2] <= data_reg[1];
    data_reg[3] <= data_reg[2];
    data_reg[4] <= data_reg[3];
end

当相同结构的赋值语句较多时,使用for语句能够简化代码,并不会影响实际综合后的电路结构。

2.2 generate...for

genvar i;

generate for(i=0;表达式2;表达式3)

begin : 起个名字

end

endgenerate

作用上:和for是一样的;

区别:

(1)generate for的循环变量必须用genvar声明,for的变量用integer整数类型声明;

(2)for只能用在always块里面,generate for可以做assign赋值,用always块话always写在generate for里;

(3)generate for后面必须给这个循环起一个名字,for不需要;

(4)generate for还可以用于例化模块;

上面的for如果用generate...for写:

代码语言:javascript
复制
always @ (posedge clk)
begin
data_reg[0] <= data_in;
end

genvar i;
generate for(i = 0; i < 4; i = i+1) begin : shift_reg
always @ (posedge clk)
begin
data_reg[i+1] <= data_reg[i];
end
end
endgenerate

3. 代码

3.1 for循环

代码语言:javascript
复制
`timescale 1ns/1ns
module gen_for_module(
    input [7:0] data_in,
    output [7:0] data_out
);

    // 2. for,必须写在always块里
    integer i;
    reg [7:0] dout_reg;
    always @ (*) begin
        for(i = 0; i < 8; i = i+1) begin
            dout_reg[i] = data_in[7-i];
        end
    end

    assign data_out = dout_reg;
endmodule

3.2 generate...for循环

代码语言:javascript
复制
`timescale 1ns/1ns
module gen_for_module(
    input [7:0] data_in,
    output [7:0] data_out
);

    // 必须使用genvar 声明循环变量
    genvar ii;
    generate for(ii = 0; ii < 8; ii = ii+1)
        begin : aaa_i
            assign data_out[ii] = data_in[7-ii];
        end
    endgenerate

endmodule

第九题——子模块例化(时序逻辑和组合逻辑两种实现方式,问题集中在时序逻辑时使用2个子模块造成答案错误,需要分析下信号的波形)

module的使用和例化,这个题的问题主要集中在为什么使用2个子模块不对,用3个才对。

实际上,对于组合逻辑实现的子模块,可以用2个,但是要打两拍才和给的波形一致。

对于时序逻辑实现的子模块,更值得大家仔细思考一下波形时序,2个确实不对,发生了比较错位,下面将详细说明。

1. 题目

在数字芯片设计中,通常把完成特定功能且相对独立的代码编写成子模块,在需要的时候再在主模块中例化使用,以提高代码的可复用性和设计的层次性,方便后续的修改。

请编写一个子模块,将输入两个8bit位宽的变量data_a,data_b,并输出data_a,data_b之中较小的数。并在主模块中例化,实现输出三个8bit输入信号的最小值的功能。

子模块的信号接口图如下:

主模块的信号接口图如下:

使用Verilog HDL实现以上功能并编写testbench验证。

输入描述:

clk:系统时钟

rst_n:异步复位信号,低电平有效

a,b,c:8bit位宽的无符号数

输出描述:

d:8bit位宽的无符号数,表示a,b,c中的最小值

2. 解析

2.1 module模块

一个.v文件中可以写多个模块module(一般是一个文件写一个module),其中主模块的名字和.v文件名相同。

代码语言:javascript
复制
module 模块名 (
端口描述
);
...
endmodule
代码语言:javascript
复制
例化:
模块名 例化名(
端口参数传递(按位置或者按名字均可)
);

比如模块名是“人”,例化名可以起名为“张三”、“李四”,这样就例化了2个“人”。

2.2 为什么调用两个子模块就不对

两种情况:

(1)子模块是纯组合逻辑

如果你的子模块用的全部是组合逻辑实现的比较,那么可以使用2个子模块,和波形不对应的原因在于提前了1个时钟。使用2个组合逻辑得到最小值后,再主模块里要对这个值打两拍,这样时序和题目答案的波形一致。

(2)子模块是时序逻辑

子模块里面的比较也选择时序逻辑寄存输出。

先说使用三个子模块的做法,a、b比较,在T+1时刻输出最小值tmp1,同时a、c比较在T+1时刻得到最小值tmp2,然后是tmp1和tmp2比较在T+2时刻得到最小值d;

一定注意这个同时的含义。T时刻输入a、b、c,T+1时刻得到tmp1和tmp2,T+2时刻得到d;

如果是使用2个子模块,T时刻比较a、b,在T+1时刻输出最小值tmp1,然后T+1时刻比较时才是c和tmp1比,在T+2时刻得到T+1时的c和tmp1的最小值;

注意,这样比较完就是拿T时刻的a、b和T+1时刻的c比较!

3. 代码

3.1 组合逻辑子模块

代码语言:javascript
复制
`timescale 1ns/1ns
module main_mod(
input clk,
input rst_n,
input [7:0]a,
input [7:0]b,
input [7:0]c,


output [7:0]d
);

wire [7:0] tmp1;    // a b 的最小值
child_mod U0(
.a        ( a ),
.b        ( b ),
.d        ( tmp1 )
);

wire [7:0] tmp2;    // a c 的最小值
child_mod U1(
.a        ( tmp1 ),
.b        ( c ),
.d        ( tmp2 )
);

reg [7:0] d_reg;
reg [7:0] d_reg2;
always @ (posedge clk or negedge rst_n)
begin
if( ~rst_n ) begin
d_reg <= 8'b0;
d_reg2 <= 8'b0;
end
else begin
d_reg <= tmp2;
d_reg2 <= d_reg;
end
end
assign d = d_reg2;

endmodule

module child_mod(
input [7:0]a,
input [7:0]b,
output [7:0]d
);
assign d = (a>b) ? b : a;
endmodule

3.2 时序逻辑子模块

代码语言:javascript
复制
`timescale 1ns/1ns
module main_mod(
input clk,
input rst_n,
input [7:0]a,
input [7:0]b,
input [7:0]c,


output [7:0]d
);

wire [7:0] tmp1;    // a b 的最小值
child_mod U0(
.clk      ( clk ),
.rst_n    ( rst_n ),
.a        ( a ),
.b        ( b ),
.d        ( tmp1 )
);

wire [7:0] tmp2;    // a c 的最小值
child_mod U1(
.clk      ( clk ),
.rst_n    ( rst_n ),
.a        ( tmp1 ),
.b        ( c ),
.d        ( tmp2 )
);

child_mod U2(
.clk      ( clk ),
.rst_n    ( rst_n ),
.a        ( tmp1 ),
.b        ( tmp2 ),
.d        ( d )
);

reg [7:0] d_reg;
always @ (posedge clk or negedge rst_n)
begin
if( ~rst_n ) begin
d_reg <= 8'b0;
end
else begin
d_reg <= tmp2;
end
end
assign d = d_reg;

endmodule

module child_mod(
input clk,
input rst_n,
input [7:0]a,
input [7:0]b,
output [7:0]d
);  
reg [7:0] d_reg;
always @ (posedge clk or negedge rst_n)
begin
if( ~rst_n ) begin
d_reg <= 8'b0;
end
else begin
if( a > b )
d_reg <= b;
else
d_reg <= a;
end
end
assign d = d_reg;

endmodule
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FPGA探索者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 题目
  • 2. 解析
  • 3. 代码
  • 1. 题目
  • 2. 解析
    • 2.1 if...else的注意要点
      • 2.2 等号运算符==和===的区别
      • 3. 代码
      • 1. 题目
      • 2. 解析
        • 2.1 for循环
          • 2.2 generate...for
          • 3. 代码
            • 3.1 for循环
              • 3.2 generate...for循环
              • 1. 题目
              • 2. 解析
                • 2.1 module模块
                  • 2.2 为什么调用两个子模块就不对
                  • 3. 代码
                    • 3.1 组合逻辑子模块
                      • 3.2 时序逻辑子模块
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档