HDLBits Coding

HDLBits Coding


前言

最近主要忙于准备数字IC方向暑期实习+秋招的事,在互联网领域卷中卷的今天,IC行业在近两年国家的大力扶持下也开始变得卷了起来,在哪卷不是卷呢......不过相对互联网而言,IC行业的好处是可以从事的时间会持久一些,但是一开始的起薪肯定不如互联网,由于芯片的开发周期较长,一般都是1~2年甚至更长时间,公司培养一个IC前端/后端工程师的周期也很长,都是N颗芯片砸钱砸出了资深的IC Engineer,所以很多IC大厂的主力军基本都是超过35岁的中年人,通过芯片研发与芯片一同成长积累更多的经验,而这种是没法通过年轻的干劲或者加班debug来替代的,毕竟流一次片起步好几百上千万美金,一般的公司谁能耗得起,因此可替代性是几乎为0。

而互联网软件的快速迭代和高度可试错性决定了他的可替代性强,比如你开发一个软件,运行出现了bug,那就加班不断debug呗,你debug的次数是可以内部上线测试或者根据用户反馈情况来进行的,这种活当然需要经验,但是相对而言,有经验的老人能干,年轻人也能干,只不过可能找bug的能力没有有经验的来的快(当然,公司的大多数核心的重要业务肯定是交给有经验的大佬完成, 不然损失的可不是一两块钱哈哈哈)相对而言,互联网有着很明显的35的坎,35岁过后,要么升上去,要么就转行,一般大厂都是招30岁以下的年轻人作为公司研发的主力,因为他们能拼能加班,而30岁过后,一般都是已经成家立业,需要考虑各种事情,身体素质也远远普遍比不上年轻人。

不管怎么说,入一行就爱一行吧,我本人兴趣是偏向硬件开发的,所以选择了数字IC方向,前路漫漫,无畏求索。目前主要是想把HDLBits网站上的Verilog练习刷一遍,这个网站挺好的,推荐入坑FPGA或者数字IC方向的前期可以刷一刷,类似于硬件版本的简化版LeetCode,网址是:https://hdlbits.01xz.net/wiki/Main_Page,写这个帖子的目的也主要是想记录一下自己的整个学习过程。

HDLBits Problems


Problem16/ 输入向量反转 Vector Reversal

Description:Given an 8-bit input vector [7:0], reverse its bit ordering.

 Solution1:stupid way

代码如下:

module top_module (
    input [7:0] in,
    output [7:0] out
);
assign {out[0],out[1],out[2],out[3],out[4],out[5],out[6],out[7]} = in;    
endmodule

这种写法一看就显得很臃肿,因为如果input 1024bit,这就得写1024遍。。。有没有更为简洁的写法?肯定是有的,Verilog语法中支持for循环,Solution2中我们采用for循环来书写

Solution2: for loop
Idea:创建一个组合逻辑always块,在块中for循环描述reverse的过程

代码如下:

module top_module (
    input [7:0] in,
    output [7:0] out
);
    integer i;
    always@(*) begin
        for(i = 0; i < 8; i++)
            out[i] = in[8-i-1];
    end
endmodule

补充:for循环中的“循环”是指代码层面的循环,而实际的硬件电路中是不存在循环这种东西的,无论是信号或者是门电路,均不存在循环一说,实际上,for循环表示的代码将被综合器按照循环次数展开成硬件电路,因此,如果要采用while、repeat这种循环的话,要可综合比如事先约定好循环次数,不能是动态可变的循环。
for循环描述了电路的行为,而不是电路的结构,因此,for循环必须置于比如 always 块这样的过程块中

Solution3: generate block

for 循环和 Verilog 中其他的几种循环语句 while ,forever,repeat 本质上都用于控制语句的执行次数,但生成块主要用于动态生成语句,例化 something(不只是例化模块),生成块与上述的过程块循环语句不同,并不是描述电路的一种行为。生成块可以例化 assign 语句,模块,信号和变量的声明以及 always initial 这样的过程块。循环生成块是生成块中的一种类型,在综合过程中同样被综合器进行编译,这个过程可以看做综合过程中动态生成更多 Verilog 代码的预处理过程。在上面的例子中,generate 块在综合的过程中,综合了 8 句 assign 赋值语句。
总的来说,**for 循环强调了对电路的行为描述,在综合的过程中循环展开,而生成块则用于综合过程中,动态生成代码,两者有本质上的不同。

参考:https://zhuanlan.zhihu.com/p/58315855

Problem18/ More Replication

Description:Given five 1-bit signals (a, b, c, d, and e), compute all 25 pairwise one-bit comparisons in the 25-bit output vector. The output should be 1 if the two bits being compared are equal.out[24] = ~a ^ a;   // a == a, so out[24] is always 1.
out[23] = ~a ^ b;
out[22] = ~a ^ c;
...
out[ 1] = ~e ^ d;
out[ 0] = ~e ^ e;

代码如下:

module top_module (
    input a, b, c, d, e,
    output [24:0] out
);

    wire [24:0] top, bottom;
    assign top    = { {5{a}}, {5{b}}, {5{c}}, {5{d}}, {5{e}} };
    assign bottom = {5{a,b,c,d,e}};
    assign out = ~top ^ bottom;    // Bitwise XNOR

    // This could be done on one line:
    // assign out = ~{ {5{a}}, {5{b}}, {5{c}}, {5{d}}, {5{e}} } ^ {5{a,b,c,d,e}};
endmodule

Problem25/ 32bit行波加法器 Ripple Carry Adder

Problem26/ 改进型32bit加法器 Carry-Select Adder

Description:One drawback of the ripple carry adder (See previous exercise) is that the delay for an adder to compute the carry out (from the carry-in, in the worst case) is fairly slow, and the second-stage adder cannot begin computing its carry-out until the first-stage adder has finished. This makes the adder slow. One improvement is a carry-select adder, shown below. The first-stage adder is the same as before, but we duplicate the second-stage adder, one assuming carry-in=0 and one assuming carry-in=1, then using a fast 2-to-1 multiplexer to select which result happened to be correct.

In this exercise, you are provided with the same module add16 as the previous exercise, which adds two 16-bit numbers with carry-in and produces a carry-out and 16-bit sum. You must instantiate three of these to build the carry-select adder, using your own 16-bit 2-to-1 multiplexer.

Connect the modules together as shown in the diagram below. The provided module add16 has the following declaration:

module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

将模块如下图所示连在一起。提供的模块add16如下:

Module cseladd.png

代码如下:

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);
    wire cout;
    wire [15:0] sum0, sum1;
    add16 add16_lower(a[15:0],b[15:0],1'b0,sum[15:0],cout);
    add16 add16_higher1(a[31:16],b[31:16],1'b0,sum0,);
    add16 add16_higher2(a[31:16],b[31:16],1'b1,sum1,);
    
    //this is 2-to-1 multiplexer
    //assign sum[31:16] = cout ? sum1:sum0;
    
    //Another expression
    always@(*) begin
        case(cout)
        	1'b1:
                sum[31:16] = sum1;
            1'b0:
                sum[31:16] = sum0;
        endcase
    end
endmodule

Problem27/ Adder-Subtractor 正负数加减法

Description: An adder-subtractor can be built from an adder by optionally negating one of the inputs, which is equivalent to inverting the input then adding 1. The net result is a circuit that can do two operations: (a + b + 0) and (a + ~b + 1). See Wikipedia if you want a more detailed explanation of how this circuit works.

Build the adder-subtractor below.

You are provided with a 16-bit adder module, which you need to instantiate twice:

module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

Use a 32-bit wide XOR gate to invert the b input whenever sub is 1. (This can also be viewed as b[31:0] XORed with sub replicated 32 times. See replication operator.). Also connect the sub input to the carry-in of the adder.

Module addsub.png

补充:

引进机器数的3种表示法:原码、补码和反码,是将符号位和数值位一起编码,机器数对应的原来数值称为真值

1.原码表示法

原码表示方法中,数值用绝对值表示,在数值的最左边用“0”和“1”分别表示正数和负数,书写成[X]原表示X的原码。

例如,在8位二进制数中,十进制数+23和-23的原码表示为: 
[+23]原=00010111
[-23]原=10010111
应注意,0的原码有两种表示,分别是“00……0”和“10……0”,都作0处理。 

2.补码表示法

一般在作两个异号的原码加法时,实际上是作减法,然后根据两数的绝对值的大小来决定符号。能否统一用加法来实现呢?这里先来看一个事实。对一个钟表,将指针从6拨到2,可以顺拨8,也可以倒拨4,用式子表示就是:6+8-12=2和6-4=2
这里12称为它的“模”。8与-4对于模12来说是互为补数。计算机中是以2为模对数值作加法运算的,因此可以引入补码,把减法运算转换为加法运算

求一个二进制数补码的方法,正数的补码与其原码相同;负数的补码是把其原码除符号位外的各位先求其反码,然后在最低位加1,通常用[X]补表示X的补码,+4和-4的补码表示为: 

[+4]补=00000100
[-4]补=11111100

例如:6 - 4 = ?

[6]补=00000110,[-4]补=11111100,所以 6-4=00000110+11111100=00000010 = 2

3.反码表示法 

正数的反码等于这个数本身,负数的反码等于其绝对值各位求反。例如: 
[+12]反=00001100
[-12]反=11110011
总结以上规律,可得到如下公式:X-Y=X+(Y的补码)=X+(Y的反码+1)

代码如下:

module top_module(
    input [31:0] a,
    input [31:0] b,
    input sub,
    output [31:0] sum
);
    wire cout;
    wire [31:0] b_temp;
    add16 add16_inst1(a[15:0], b_temp[15:0], sub, sum[15:0],cout);
    add16 add16_inst2(a[31:16], b_temp[31:16], cout, sum[31:16],);
    
    assign b_temp = {32{sub}} ^ b;

endmodule

参考:https://blog.csdn.net/llf021421/article/details/16873591

未完待续。。。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值