前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >题解 | Verilog刷题解析及对应笔试面试注意点【1-5】(涉及复位、有符号数问题等)

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

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

在B站录制了试题讲解视频,更新题目解析文章本文更新了第1-5题

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

刷题地址:https://www.nowcoder.com/exam/oj?tab=Verilog%E7%AF%87&topicId=302&fromPut=pc_zh_s_1540795715

第一题——四选一选择器(三目运算符?:和case语句)

第二题——T触发器(异步复位和同步复位)

第三题——奇偶校验(实际上这个题应该是奇偶检测)

第四题——移位拼接乘法(有符号数和无符号数的乘法和除法)

第五题——位拆分

视频:

文章:

第一题——四选一选择器

1. 题目

四选一选择器。

2. 代码

2.1 写法1——三目运算符?:

assign语句+三目运算符?:

代码语言:javascript
复制
`timescale 1ns/1ns

module mux4_1(
input [1:0]d1,d2,d3,d0,
input [1:0]sel,
output [1:0]mux_out
);

assign mux_out = (sel == 2'b00) ? d3 : ((sel == 2'b01) ? d2 : (sel == 2'b10) ? d1 : d0);

endmodule

2.2 写法2——case语句

代码语言:javascript
复制
`timescale 1ns/1ns

module mux4_1(
input [1:0]d1,d2,d3,d0,
input [1:0]sel,
output [1:0]mux_out
);

reg [1:0] mux_out_reg;
always @ (*)
begin
    case(sel)
    2'b00:mux_out_reg = d3;
    2'b01:mux_out_reg = d2;
    2'b10:mux_out_reg = d1;
    2'b11:mux_out_reg = d0;
    default : mux_out_reg = d0;
    endcase
end

assign mux_out = mux_out_reg;

endmodule

3. 解析

3.1 三目运算符

三目运算符?:,使用方法:

代码语言:javascript
复制
d = c ? a : b;

其中,a、b、c均可以是表达式也可以是变量等,等效于if...else语句。

if...else只能在always语句描述中使用,所以有时候为了在描述组合逻辑时,一般就用?:来实现这种条件判断效果。

3.2 case语句

代码语言:javascript
复制
case语句:
case(表达式)
条件分支1: xxx;
条件分支2: xxx;
...
条件分支n: xxx;
缺省default: xxx;
endcase

case的使用注意点:

(1)要在always块里使用,如果是用always块描述组合逻辑,注意括号里的敏感变量列表都是电平触发,并且赋值时都要用阻塞赋值“=”;

(2)always块里的变量必须声明成reg类型,当然声明成reg类型不代表一定会综合成寄存器,只是语法要求always块里要这样;

(3)always块描述组合逻辑时,用*可以代表所有always块内敏感信号;

分支条件要写全,最好补齐default缺省条件,不然在组合逻辑中可能会由于条件不全导致出现锁存器Latch

第二题——T触发器

1. 题目

用verilog实现两个串联的异步低电平复位的T触发器的逻辑。这个题目的重点是要关注异步低电平复位

不得不读的 FPGA 设计白皮书——Xilinx FPGA 复位策略白皮书翻译(WP272)【FPGA探索者】

联发科数字IC简答题(9)——异步复位同步释放问题

2. 解析

2.1 T触发器

边沿T触发器:输入为1时下个时钟触发沿输出翻转;输入为0时下个时钟触发沿输出保持。

边沿D触发器,输入为1时下个时钟触发沿输出为1,输入为0时下个时钟触发沿输出为0。

所以关于T触发器:

代码语言:javascript
复制
if(data_in == 1’b1)
data_out <= ~data_out;
else
data_out <= data_out;

2.2 异步复位

异步复位,说明复位是异步的,和时钟触发边沿无关,复位信号一旦来临就使得寄存器进行复位操作,复位信号出现在always块的敏感列表里。

对于异步的低电平复位,以下降沿作为触发边沿(高电平变为低电平的时刻),并且触发后判断复位是否为低电平,即:

always @ (posedge clk or negedge rst)

begin

if( ~rst )

...;

else

...;

end

对于异步的高电平复位,以上升沿作为触发边沿(低电平变为高电平的时刻),并且触发后判断复位是否为高电平,即:

always @ (posedge clk or posedge rst)

begin

if( rst )

...;

else

...;

end

2.3 同步复位

同步复位时,复位与时钟触发沿有关,所以在always的敏感变量中,只有时钟触发边沿,然后根据高电平或者低电平再判断复位电平。

同步低电平复位:

always @ (posedge clk)

begin

if( ~rst )

...;

else

...;

end

同步高电平复位:

always @ (posedge clk)

begin

if( rst )

...;

else

...;

end

2.4 同步复位和异步复位的优缺点

异步复位:反应快,复位电平可以小于一个时钟周期,有些触发器只有异步复位端口;

同步复位:稳定,不易受毛刺干扰,有些模块只有同步复位端口;

3. 代码

代码语言:javascript
复制
`timescale 1ns/1ns
module Tff_2 (
input wire data, clk, rst,
output reg q  
);

// 1. 复位
//2. T触发器,D触发器
reg q1;
always @ (posedge clk or negedge rst)
begin
    if(!rst) begin
        q1 <= 1'b0;
    end
    else begin
        if( data )
            q1 <= ~q1;
        else
            q1 <= q1;
    end
end

always @ (posedge clk or negedge rst)
begin
    if(!rst) begin
        q <= 1'b0;
    end
    else begin
        if( q1 )
            q <= ~q;
        else
            q <= q;
    end
end

endmodule

第三题——奇偶校验(奇偶检测)

1. 题目

用verilog实现对输入的32位数据进行奇偶校验,根据sel输出校验结果(sel=1输出奇校验,sel=0输出偶校验)。

代码语言:javascript
复制
`timescale 1ns/1ns
module odd_sel(
input [31:0] bus,
input sel,
output check
);
//*************code***********//
 
//*************code***********//
endmodule

2. 解析

2.1 奇偶校验

通常所说的奇偶校验:

奇校验:对输入数据添加1位0或者1,使得添加后的数包含奇数个1;

比如100,有奇数个1,那么奇校验结果就是0,这样补完0以后还是奇数个1;

奇校验:对输入数据添加1位0或者1,使得添加后的数包含偶数个1;

回到这个题目,应该是出题人搞反了,按照出题的意思,应该不能叫奇偶校验,应该是叫奇偶检测

奇检测:输入的数据里有奇数个1就输出1;

偶检测:输入的数据里有偶数个1就输出1;

2.2 单目运算符

红框里的内容在视频讲解时有误,已更正。

单目运算符使用时,输入的数据的每一位进行运算,最后结果一定是1 bit的。

用处:

3. 代码

代码语言:javascript
复制
`timescale 1ns/1ns
module odd_sel(
input [31:0] bus,
input sel,
output check
);
//*************code***********//
wire check_tmp;

    // 单目运算符
    assign check_tmp = ^bus;
  //  assign check = (sel == 1'b1) ? check_tmp : ~check_tmp;

    reg check_reg;
    always @ (*) begin
        if(sel) begin
            check_reg = check_tmp;
        end
        else begin
            check_reg = ~check_tmp;
        end
    end

    assign check = check_reg;

//*************code***********//
endmodule

第四题——移位拼接乘法

1. 题目

已知d为一个8位数,请在每个时钟周期分别输出该数乘1/3/7/8,并输出一个信号通知此时刻输入的d有效(d给出的信号的上升沿表示写入有效)。

2. 解析

2.1 移位运算实现乘法

移位运算实现乘法和无符号除法

位拼接运算符实现拼接和复制

位拼接运算符实现乘法和除法

关于有符号数和无符号数,可以参考【FPGA探索者】的相关文章【Verilog学习笔记——有符号数的乘法和加法】:

Verilog学习笔记——有符号数的乘法和加法

2.2 题目波形分析进行寄存

如下图所示的红框和绿框内的数据非常关键。如果对输入的d在连续的4个时钟周期内分别进行d*1、d*3、d*7和d*8操作,那么当出现如红框内所示的6时,这个数据只持续了1个clk,显然这时候做的操作是:

6*1、128*3、129*7、129*8,和预期不符。

如何保证做的移位乘法都是基于第一次的输入呢?

答案:加一个寄存器,对输入寄存

d_reg <= d;

后面的*3、*7、*8均对d_reg操作,执行完后再根据输入d更新d_reg。

3. 代码

代码语言:javascript
复制
`timescale 1ns/1ns
module multi_sel(
input [7:0]d ,
input clk,
input rst,
output reg input_grant,
output reg [10:0]out
);
//*************code***********//
    reg [1:0] count;    // 0 1 2 3
    always @ (posedge clk or negedge rst)
        begin
            if(~rst) begin
                count <= 2'b0;
            end
            else begin
                count <= count + 1'b1;
            end
        end

    // FSM
    reg [7:0] d_reg;
        always @ (posedge clk or negedge rst)
        begin
            if(~rst) begin
                out <= 11'b0;
                input_grant <= 1'b0;
                d_reg <= 8'b0;
            end
            else begin
                case( count )
                    2'b00 : begin
                        out <= d;
                        d_reg <= d;
                        input_grant <= 1'b1;
                    end
                    2'b01 : begin
                        out <= d_reg + {d_reg, 1'b0};    // *1 + *2
                        input_grant <= 1'b0;
                    end
                    2'b10 : begin
                        out <= d_reg + {d_reg, 1'b0} + {d_reg, 2'b0};
                        input_grant <= 1'b0;
                    end
                    2'b11 : begin
                        out <= {d_reg, 3'b0};
                        input_grant <= 1'b0;
                    end
                    default : begin
                        out <= d;
                        input_grant <= 1'b0;
                    end
                endcase
            end
        end
//*************code***********//
endmodule

第五题——位拆分

1. 题目

输入16位数据d[15:0],按照sel选择输出,并输出valid_out信号(在不输出时候拉低)

sel = 0:不输出且只有此时的输入有效

sel = 1:输出d[3:0]+d[7:4]

sel = 2:输出d[3:0]+d[11:8]

sel = 3:输出d[3:0]+d[15:12]

2. 解析

本题目的sel选择输出比较简单,可以用if...else if...else来完成,也可以用第1题提到的case语句实现。

问题的关键在于还是要像第4题一样做寄存。从波形上没有特别明显看出,但是要注意条件:

只有sel=0时的输入有效!所以在sel=0时用d_reg寄存,而sel不等于0时,相加操作用的是寄存数据d_reg拆分相加,而不是用当前的输入d。

这时候我们再仔细观察下波形,发现确实需要寄存。

3. 代码

代码语言:javascript
复制
`timescale 1ns/1ns

module data_cal(
input clk,
input rst,
input [15:0]d,
input [1:0]sel,

    output [4:0]out, // wire
output validout // wire
);
//*************code***********//

    reg [15:0] d_reg;
    wire [3:0] d0;
    wire [3:0] d1;
    wire [3:0] d2;
    wire [3:0] d3;
    assign d0 = d_reg[3:0];
    assign d1 = d_reg[7:4];
    assign d2 = d_reg[11:8];
    assign d3 = d_reg[15:12];
    reg [4:0] out_reg;
    reg validout_reg;
    always @ (posedge clk or negedge rst)
        begin
            if( ~rst ) begin
                out_reg <= 5'b0;
                validout_reg <= 1'b0;
                d_reg <= 16'b0;
            end
            else begin
                case( sel )
                    2'b00 : begin
                        d_reg <= d;
                        out_reg <= 5'b0;
                        validout_reg <= 1'b0;    
                    end
                    2'b01 : begin
                        d_reg <= d_reg;
                        out_reg <= d_reg[3:0] + d_reg[7:4];// d0 + d1;
                        validout_reg <= 1'b1;    
                    end
                     2'b10 : begin
                         d_reg <= d_reg;
                        out_reg <= d0 + d2;
                        validout_reg <= 1'b1;    
                    end
                     2'b11 : begin
                         d_reg <= d_reg;
                        out_reg <= d0 + d3;
                        validout_reg <= 1'b1;    
                    end
                    default : begin
                        out_reg <= 5'b0;
                        validout_reg <= 1'b0;  
                    end
                endcase
            end
        end

    assign out = out_reg;
    assign validout = validout_reg;

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 题目
  • 2. 代码
    • 2.1 写法1——三目运算符?:
    • 3. 解析
      • 3.1 三目运算符
        • 3.2 case语句
        • 1. 题目
        • 2. 解析
          • 2.1 T触发器
            • 2.2 异步复位
              • 2.3 同步复位
                • 2.4 同步复位和异步复位的优缺点
                • 3. 代码
                • 1. 题目
                • 2. 解析
                  • 2.1 奇偶校验
                    • 2.2 单目运算符
                    • 3. 代码
                    • 1. 题目
                    • 2. 解析
                      • 2.1 移位运算实现乘法
                        • 2.2 题目波形分析进行寄存
                        • 3. 代码
                        • 1. 题目
                        • 2. 解析
                        • 3. 代码
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档