前言:
本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析、代码及波形,所有代码均经过本人验证。
目录如下:
1.数字IC手撕代码-分频器(任意偶数分频)
2.数字IC手撕代码-分频器(任意奇数分频)
3.数字IC手撕代码-分频器(任意小数分频)
4.数字IC手撕代码-异步复位同步释放
5.数字IC手撕代码-边沿检测(上升沿、下降沿、双边沿)
6.数字IC手撕代码-序列检测(状态机写法)
7.数字IC手撕代码-序列检测(移位寄存器写法)
8.数字IC手撕代码-半加器、全加器
9.数字IC手撕代码-串转并、并转串
10.数字IC手撕代码-数据位宽转换器(宽-窄,窄-宽转换)
11.数字IC手撕代码-有限状态机FSM-饮料机
12.数字IC手撕代码-握手信号(READY-VALID)
13.数字IC手撕代码-流水握手(利用握手解决流水线断流、反压问题)
14.数字IC手撕代码-泰凌微笔试真题
15.数字IC手撕代码-平头哥技术终面手撕真题
16.数字IC手撕代码-兆易创新笔试真题
17.数字IC手撕代码-乐鑫科技笔试真题(4倍频)
18.数字IC手撕代码-双端口RAM(dual-port-RAM)
...持续更新
更多手撕代码题可以前往 数字IC手撕代码--题库
目录
题目
概念解释
实现原理
代码
testbench
波形
题目
以上是泰凌微公司的笔试真题,手撕一个代码,题目描述如下:
假设每个clock cycle输入是一个3-bit数据信号,现在需要按次序整形成5-bit数据信号输出,并给出valid信号。其中LSB First,请用Verilog/VHDL给出代码。
sample data:
输入:000,110,011,011,010,010,011... 即:(000|11 0|011 |0 11|010|010|01 1)
输出:00011,00110,11010,01001,1... 即:(000 11|0 011 0|11010| 010 01|1)
该题目,仔细阅读就会发现,序列都是同一条序列,分割方式不同;说白了其实就是一个位宽转换题,然后加握手信号。实现一个含握手的3-5bit的位宽转换模块。这个题目,华子也考过类似的,或者说很多公司爱考这玩意。
概念解释
下面解释下,题目中出现的概念
1、LSB(Least Significant Bit)--最低有效位
LSB代表二进制中最小的单位,可以用来指示数字很小的变化。也就是说,LSB是一个二进制数字中的第0位(即最低位),具有权值为2^0,可以用来检测数的奇偶性。
2、MSB(Most Significant Bit)--最高有效位
MSB代表一个n位二进制数字中的n-1位,具有最高的权值2^(n-1).对于有符号的二进制数,负数采用反码或补码形式,此时MSB用来表示符号,msb为1表示负数,0表示正数。
实现原理
好的,解释完概念,题目的意思就非常明白了,实现一个含有握手信号的3-5 bit位宽转换器,输入为3bit,输出5bit,且输入的3bit从低位开始排,即输入000,110时,第一个5bit输出的是000 11,而不是10 000,也就是题目说的LSB First。
思路分析
① 输入第一个3bit,无法输出;
② 输入第二个3bit,此时存数6bit,可以输出一次5bit,余下1bit;valid=1;
③ 输入第三个3bit,此时由于输出过一次5bit,余下1bit,加上这第三次输入的3bit,此时存数4bit,无法输出;
④ 输入第四个3bit,此时存数7bit,可以输出一次5bit,余下2bit;valid=1;
⑤ 输入第五个3bit,此时存数5bit,可以输出一次5bit,存数为0bit,状态循环可回到初始状态。valid=1;
显然每五次输入是一个轮回,这是因为15bit是3bit和5bit 的最小公倍数,因此每输入15/3=5次,就输出15/5=3次。如此反复循环。
根据分析我们看到,输出有效的时间点是第二次输入后、第四次输入后、第五次输入后,因此我们可以用一个计数器counter来作为valid信号的判据,当计数器为2、4、5时,valid都拉高即可。理清思路,代码就很好写了。
代码:
计数器,来计入总共输入了几次3bit数据:
reg [2:0] counter;
always @(posedge clk)begin //handshake counter
if(!rstn)begin
counter <= 3'd0;
end
else if(ready_i && valid_i && (counter<=3))begin
counter <= counter + 1'b1;
end
else if(ready_i && valid_i && (counter==4))begin
counter <= 3'd0;
end
en
当输入第二次、第四次、第五次时,valid信号拉高:
reg [6:0] data_store;
always @(posedge clk)begin
if(!rstn)begin
valid_o_temp <= 1'b0;
end
else if(ready_i && valid_i)begin
if((counter==1) || (counter==3) || (counter==4))begin //valid_o output
valid_o_temp <= 1'b1;
end
else begin
valid_o_temp <= 1'b0;
end
case(counter)
3'd0:data_store[6:4] <= data_in; //store 3bit no output
3'd1:data_store <= {data_store[6:4],data_in,1'b0}; //store 6bit but output 5bit
3'd2:data_store <= {data_store[3:0],data_in}; //store 4bit no output
3'd3:data_store <= {data_store[3:0],data_in}; //store 7bit output 5bit
3'd4:data_store <= {data_store[1:0],data_in,2'd0}; //store 5bit output 5bit
endcase
end
end
case语句内的作用是将数据进行移位。因为最多情况下是存储7bit数据,然后输出5bit,所以存储输入数据的空间只需要7bit就够了。用7bit 的 data_store来存储输入数据;
第一次输入的3bit数据存储在data_store的高3位,不输出5bit数据;
第二次输入的3bit数据存储在data_store的高4-6位。此时,valid信号拉高,然后输出5bit 数据 data_out 等于data_store的高5bit即可;
第三次输入的3bit数据,让data_store左移三位,然后存储在低三位,不输出5bit数据;
第四次输入的3bit数据,让data_store左移三位,然后存储在低三位,此时7bit空间存满,因为进行了两次“左移三位”,所以此时7bit数据的最高位,就是原来7bit数据的最低位,也就是在第二次输入数据时,存储6bit输出5bit后,剩下的那1bit数据。再输出高5bit数据;余2bit
低五次输入的3bit数据,与上一阶段余下的2bit数据,凑5bit,放在data_store的高5位,输出。
代码
module tailinwei#(
)(
input clk ,
input rstn ,
input [2:0] data_in ,
output [4:0] data_out ,
input ready_i ,
input valid_i ,
output ready_o ,
output valid_o
);
reg valid_o_temp,valid_onebeat;
reg [2:0] counter;
always @(posedge clk)begin //handshake counter
if(!rstn)begin
counter <= 3'd0;
end
else if(ready_i && valid_i && (counter<=3))begin
counter <= counter + 1'b1;
end
else if(ready_i && valid_i && (counter==4))begin
counter <= 3'd0;
end
end
always @(posedge clk)begin
if(ready_i)begin
valid_onebeat <= valid_i; //valid_i one beat
end
end
reg [6:0] data_store;
always @(posedge clk)begin
if(!rstn)begin
valid_o_temp <= 1'b0;
end
else if(ready_i && valid_i)begin
if((counter==1) || (counter==3) || (counter==4))begin //valid_o output
valid_o_temp <= 1'b1;
end
else begin
valid_o_temp <= 1'b0;
end
case(counter)
3'd0:data_store[6:4] <= data_in; //store 3bit no output
3'd1:data_store <= {data_store[6:4],data_in,1'b0}; //store 6bit but output 5bit
3'd2:data_store <= {data_store[3:0],data_in}; //store 4bit no output
3'd3:data_store <= {data_store[3:0],data_in}; //store 7bit output 5bit
3'd4:data_store <= {data_store[1:0],data_in,2'd0}; //store 5bit output 5bit
endcase
end
end
assign data_out = data_store[6:2]; // get high 5bit to output
assign valid_o = valid_o_temp && valid_onebeat;
endmodule
testbench
tb:整个testbench部分的激励,就按照题目的意思生成。
module tailinwei_tb();
reg clk,rstn;
always #5 clk = ~clk;
wire ready_o,valid_o;
reg ready_i,valid_i;
reg [2:0] data_in;
wire [4:0] data_out;
initial begin
clk <= 1'b0;
rstn <= 1'b0;
ready_i <= 1'b1;
valid_i <= 1'b0;
#20
rstn <= 1'b1;
data_in <= 3'b000;
valid_i <= 1'b1;
#10 data_in <= 3'b110;
#10 data_in <= 3'b011;
#10 data_in <= 3'b011;
#10 data_in <= 3'b010;
#10 data_in <= 3'b010;
#10 data_in <= 3'b011;
#10
valid_i <= 1'b0;
#50
$stop();
end
tailinwei u_tailinwei(
.clk(clk),
.rstn(rstn),
.data_in(data_in),
.data_out(data_out),
.ready_i(ready_i),
.valid_i(valid_i),
.ready_o(ready_o),
.valid_o(valid_o)
);
endmodule
波形
输入:000,110,011,011,010,010,011... 即:(000|11 0|011 |0 11|010|010|01 1)
输出:00011,00110,11010,01001,1... 即:(000 11|0 011 0|11010| 010 01|1)
每次输出数据时,valid信号有效,如波形所示。
更多手撕代码题可以前往 数字IC手撕代码--题库