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 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.
补充:
引进机器数的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