牛客刷题链接
//4、移位运算与乘法
`timescale 1ns/1ns
module multi_sel(
input [7:0]d ,
input clk,//200MHZ,5ns
input rst,
output reg input_grant,
output reg [10:0]out
);
reg [1:0] cnt_3;
always@(posedge clk or negedge rst)begin
if(!rst)begin
cnt_3 <= 2'd0;
end
else if(cnt_3 == 2'd3)begin
cnt_3 <= 2'd0;
end
else
cnt_3 <= cnt_3 + 2'd1;
end
reg [7:0] d_reg;
always@(posedge clk or negedge rst)begin
if(!rst)begin
input_grant = 1'd0;
d_reg = 8'd0;
out = 8'd0;
end
else begin
case(cnt_3)
2'd0:begin
input_grant = 1'd1;
d_reg = d;//寄存数据
out = d;
end
2'd1:begin
input_grant = 1'd0;
out = d_reg + (d_reg<<1'd1);//3
end
2'd2:begin
input_grant = 1'd0;
out = d_reg + (d_reg<<1'd1) + (d_reg<<2'd2) ;//7
end
2'd3:begin
input_grant = 1'd0;
out = (d_reg<<3'd3);//8
end
default:;
endcase
end
end
endmodule
`timescale 1ns/1ns
module data_cal(
input clk,
input rst,
input [15:0]d,
input [1:0]sel,
output reg [4:0]out,
output reg validout
);
//*************code***********//
reg [15:0] d_reg;
always@(posedge clk or negedge rst)begin
if(!rst)begin
out <= 5'd0;
validout <= 1'd0;
d_reg <= 16'd0;
end
else
case(sel)
2'd0: begin
validout <= 1'd0;
d_reg <= d;//锁存数据
out <= 5'd0;
end
2'd1: begin
validout <= 1'd1;
out <= d_reg[3:0] + d_reg[7:4];
end
2'd2: begin
validout <= 1'd1;
out <= d_reg[3:0] + d_reg[11:8];
end
2'd3: begin
validout <= 1'd1;
out <= d_reg[3:0] + d_reg[15:12];
end
default:;
endcase
end
//*************code***********//
endmodule
根据指示信号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。
因为输入输出都已经直接定义了signed有符号数类型,所以直接相加、相减也没有问题,不会出现运算错误。
有符号数+有符号数=有符号数,但是如果表达式中有一个无符号数,则所有的操作数都会被强行转换为无符号数
两种解决方法
:
(1)涉及到有符号数运算时,和有符号相关的输入、输出、中间变量均定义成signed有符号数,这样全部遵循有符号数运算规则;
(2)用位拼接符补齐符号位;
// VL6 多功能数据处理器
// 1.有符号数的减法运算
// 2.选择器
// 3.不同位宽赋值
`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'd0;
end
case(select)
2'd0: c <= a;
2'd1: c <= b;
2'd2: c <= a + b;
2'd3: c <= a - b;
default:;
endcase
end
endmodule
参考笔记,总结详细
作用上
:
和for是一样的;
区别
:
(1)generate for的循环变量必须用genvar声明,for的变量可以用reg、integer整数等多种类型声明;
(2)for只能用在always块里面,generate for可以做assign赋值,用always块话always写在generate for里;
(3)generate for后面必须给这个循环起一个名字,for不需要;
(4)generate for还可以用于例化模块
格式
:
genvar i;
generate
for(i=0;表达式2;表达式3)
begin :起个名字
…;
end
endgenerate
// VL8 使用generate…for语句简化代码
`timescale 1ns/1ns
module gen_for_module(
input [7:0] data_in,
output [7:0] data_out
);
genvar i;
generate
for (i = 0; i < 8 ; i=i+1)
begin: myloop
assign data_out[i] = data_in[7-i];
end
endgenerate
endmodule
等价于
因此for循环里的语句也是并行的
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
`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
);
wir [7:0] min_ab;
wir [7:0] min_ac;
wir [7:0] min_abc;
sub_mod sub_mod_inst_01(
.clk(clk),
.rst_n(rst_n),
.a(a),
.b(b),
.d(min_ab)
);
sub_mod sub_mod_inst_02(
.clk(clk),
.rst_n(rst_n),
.a(a),
.b(c),
.d(min_ac)
);
sub_mod sub_mod_inst_03(
.clk(clk),
.rst_n(rst_n),
.a(min_ab),
.b(min_ac),
.d(min_abc)
);
assign d = min_abc;
endmodule
//子模块
module sub_mod(
input clk,
input rst_n,
input [7:0]a,
input [7:0]b,
output reg[7:0]d
);
always@(posedge clk or negedge rst_n)begin
if(!rst_n )begin
d <= 8'd0;
end
else if(a>b)begin
d <= b;
end
else if(a<b)
d <= a;
else
d <= d;
end
endmodule
function笔记
实现向量位转换
HDLBits类似题
`timescale 1ns/1ns
module function_mod(
input clk,
input rst_n,
input [3:0]a,
input [3:0]b,
output [3:0]c,
output [3:0]d
);
assign c = rst_n ? change_bit(a) : 1'd0;
assign d = rst_n ? change_bit(b) : 1'd0;
function [3:0]change_bit;
input [3:0]data_in;
integer i;
for (i = 0; i < 4 ; i=i+1)
begin: myloop
change_bit[i] = data_in[3-i];
end
endfunction
endmodule
题目中规定了要用门级描述:参考答案
首先根据真值表写出表达式
然后画出对应的电路图
最后可以用Verilog描述出上面的电路
// VL11 4位数值比较器电路
`timescale 1ns/1ns
module comparator_4(
input [3:0] A ,
input [3:0] B ,
output wire Y2 , //A>B
output wire Y1 , //A=B
output wire Y0 //A
);
// 方法一:使用比较大小的方法,虽然能运行出来,但是题目要求是欧诺个门级描述
// 因此最好不适用比较大小的方法
/* assign Y2 = ((A[3]>B[3])||(A[2]>B[2])||((A[1]>B[1]) && (A[2]==B[2]) && (A[3]==B[3]))||((A[0]>B[0])&&(A[2]==B[2])&&(A[1]==B[1]) && (A[3]==B[3])))?1'd1:1'd0;
assign Y0 = ((A[3]
//方法二
not iv0(iv0_o, B[0]),
iv1(iv1_o, B[1]),
iv2(iv2_o, B[2]),
iv3(iv3_o, B[3]),
iv4(iv4_o, A[0]),
iv5(iv5_o, A[1]),
iv6(iv6_o, A[2]),
iv7(iv7_o, A[3]);
and ad0(
, iv0_o, A[0]),
ad1(ad1_o, iv1_o, A[1]),
ad2(ad2_o, iv2_o, A[2]),
ad3(ad3_o, iv3_o, A[3]),
ad4(ad4_o, ad0_o, xnr0_o, xnr1_o, xnr2_o),
ad5(ad5_o, ad1_o, xnr1_o, xnr2_o),
ad6(ad6_o, ad2_o, xnr2_o),
ad7(ad7_o, iv4_o, B[0]),
ad8(ad8_o, iv5_o, B[1]),
ad9(ad9_o, iv6_o, B[2]),
ad10(ad10_o, iv7_o, B[3]),
ad11(ad11_o, ad7_o, xnr0_o, xnr1_o, xnr2_o),
ad12(ad12_o, ad8_o, xnr1_o, xnr2_o),
ad13(ad13_o, ad9_o, xnr2_o),
ad14(Y1, xnr2_o, xnr1_o, xnr0_o, xnr3_o);
xnor xnr0(xnr0_o, A[1], B[1]),
xnr1(xnr1_o, A[2], B[2]),
xnr2(xnr2_o, A[3], B[3]),
xnr3(xnr3_o, A[0], B[0]);
or or0(Y2, ad3_o, ad6_o, ad5_o, ad4_o),
or1(Y0, ad10_o, ad13_o, ad12_o, ad11_o) ;
endmodule
编码器
:输入多个0、1编码信号,输出编码后的二进制
普通和优先
:
1. 普通:输入只有1个编码信号
2. 优先:输入只有多个编码信号
`timescale 1ns/1ns
module encoder_0(
input [8:0] I_n ,
output reg [3:0] Y_n
);
always@(*) begin
casez (I_n)
9'b1_1111_1111: Y_n = 4'b1111;
9'b0_????_????: Y_n = 4'b0110;
9'b1_0???_????: Y_n = 4'b0111;
9'b1_10??_????: Y_n = 4'b1000;
9'b1_110?_????: Y_n = 4'b1001;
9'b1_1110_????: Y_n = 4'b1010;
9'b1_1111_0???: Y_n = 4'b1011;
9'b1_1111_10??: Y_n = 4'b1100;
9'b1_1111_110?: Y_n = 4'b1101;
9'b1_1111_1110: Y_n = 4'b1110;
default: Y_n = 4'b0000;
endcase
end
endmodule
10个按键分别对应十进制数0-9,按键9的优先级别最高;按键悬空时,按键输出高电平,按键按下时,按键输出低电平;键盘编码电路的输出是8421BCD码。
要求:键盘编码电路要有工作状态标志,以区分没有按键按下和按键0按下两种情况
思路
:
//VL14 用优先编码器①实现键盘编码电路
`timescale 1ns/1ns
module key_encoder(
input [9:0] S_n ,
output reg[3:0] L ,
output wire GS
);
always@(*)begin
casez(S_n)
10'b0????_?????: L = 4'b1001;
10'b10???_?????: L = 4'b1000;
10'b110??_?????: L = 4'b0111;
10'b1110?_?????: L = 4'b0110;
10'b11110_?????: L = 4'b0101;
10'b11111_0????: L = 4'b0100;
10'b11111_10???: L = 4'b0011;
10'b11111_110??: L = 4'b0010;
10'b11111_1110?: L = 4'b0001;
10'b11111_11110: L = 4'b0000;
default:L = 4'b0000;
endcase
end
assign GS = (S_n == 10'b11111_11111)?1'd1:1'd0;
endmodule
和数电课本上的差不多,只是没有对信号进行取反 数电第六版闫石P(155页)
//VL15 优先编码器Ⅰ
`timescale 1ns/1ns
module encoder_83(
input [7:0] I ,
input EI ,
output reg [2:0] Y ,
output wire GS ,
output wire EO
);
always@(*)begin
case(EI)
1'd0:
Y = 3'b000;
1'd1:
casez(I)
8'b0000_0000: Y = 3'b000;
8'b1???_????: Y = 3'b111;
8'b01??_????: Y = 3'b110;
8'b001?_????: Y = 3'b101;
8'b0001_????: Y = 3'b100;
8'b0000_1???: Y = 3'b011;
8'b0000_01??: Y = 3'b010;
8'b0000_001?: Y = 3'b001;
8'b0000_0001: Y = 3'b000;
default:Y = 3'b000;
endcase
endcase
end
assign EO = EI&~I[7]&~I[6]&~I[5]&~I[4]&~I[3]&~I[2]&~I[1]&~I[0];
assign GS = EI&(I[7] | I[6] | I[5] | I[4] | I[3] | I[2] | I[1] | I[0]);
/* assign GS =(I[0]) & EI;
assign EO =(yu) & EI;
*/
/* reg [7:0]add = 'd0;
genvar i;
generate
for(i = 0 ; i<10 ; i = i+1)begin: add_I
add = add + I[i];
end
endgenerate
reg [7:0]yu = 'd0;
genvar j;
generate
for(j = 0 ; j<10 ; j = j+1)begin: yu_I
yu = yu & (~I[j]);
end
endgenerate */
endmodule
//VL16 使用8线-3线优先编码器Ⅰ实现16线-4线优先编码器
`timescale 1ns/1ns
module encoder_83(
input [7:0] I ,
input EI ,
output wire [2:0] Y ,
output wire GS ,
output wire EO
);
assign Y[2] = EI & (I[7] | I[6] | I[5] | I[4]);
assign Y[1] = EI & (I[7] | I[6] | ~I[5]&~I[4]&I[3] | ~I[5]&~I[4]&I[2]);
assign Y[0] = EI & (I[7] | ~I[6]&I[5] | ~I[6]&~I[4]&I[3] | ~I[6]&~I[4]&~I[2]&I[1]);
assign EO = EI&~I[7]&~I[6]&~I[5]&~I[4]&~I[3]&~I[2]&~I[1]&~I[0];
assign GS = EI&(I[7] | I[6] | I[5] | I[4] | I[3] | I[2] | I[1] | I[0]);
//assign GS = EI&(| I);
endmodule
module encoder_164(
input [15:0] A ,
input EI ,
output wire [3:0] L ,
output wire GS ,
output wire EO
);
wire EO1 ;
wire [2:0] Y0 ;
wire [2:0] Y1 ;
wire GS0 ;
encoder_83 U0(
.I (A[7:0]),
.EI (EO1 ),
.Y (Y0 ),
.GS (GS0 ),
.EO (EO )
);
encoder_83 U1(
.I (A[15:8]),
.EI (EI ),
.Y (Y1 ),
.GS (GS1 ),
.EO (EO1 )
);
assign L[3] = GS1;
assign L[2] = Y1[2] | Y0[2];
assign L[1] = Y1[1] | Y0[1];
assign L[0] = Y1[0] | Y0[0];
assign GS = GS1 | GS0;
endmodule
//VL21 根据状态转移表实现时序电路
`timescale 1ns/1ns
module seq_circuit(
input A ,
input clk ,
input rst_n,
output wire Y
);
reg Q0_n1;
reg Q1_n1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n )begin
Q0_n1 <= 1'd0;
Q1_n1 <= 1'd0;
end
else begin
Q0_n1 <= ~Q0_n1;
Q1_n1 <= A ^ Q0_n1 ^ Q1_n1;
end
end
assign Y = Q0_n1 & Q1_n1;
endmodule
// VL22 根据状态转移图实现时序电路
`timescale 1ns/1ns
module seq_circuit(
input C ,
input clk ,
input rst_n,
output reg Y
);
parameter S0 = 2'b00,
S1 = 2'b01,
S2 = 2'b10,
S3 = 2'b11;
reg [1:0] state;
reg [1:0] next_state;
always@(posedge clk or negedge rst_n)begin
if(!rst_n )begin
state <= S0;
end
else
state <= next_state;
end
always@(*)begin
case(state)
S0:
if(C)
next_state = S1;
else
next_state = S0;
S1:
if(~C)
next_state = S3;
else
next_state = S1;
S3:
if(C)
next_state = S2;
else
next_state = S3;
S2:
if(~C)
next_state = S0;
else
next_state = S2;
default:next_state = S0;
endcase
end
//综合出来后发现Y 延迟 一个周期,因此要用组合逻辑
/* always@(posedge clk or negedge rst_n)begin
if(!rst_n )begin
Y <= 1'd0;
end
else if(((state == S2) && C) || ((state == S3)))begin
Y <= 1'd1;
end
else
Y <= 1'd0;
end */
always@(*)begin
case(state)
S2: if(C)
Y = 1'd1;
else
Y = 1'd0;
S3: Y = 1'd1;
default:Y = 1'd0;
endcase
end
endmodule
推荐阅读:博客-介绍ROM声明、双端口ROM-FIFO
要求
:实现一个深度为8,位宽为4bit的ROM,数据初始化为0,2,4,6,8,10,12,14。可以通过输入地址addr,输出相应的数据data
重点
:
reg [3:0] SAVE [7:0]
表示:reg [数据位宽] SAVE [SAVE寄存器的个数]//VL23 ROM的简单实现
`timescale 1ns/1ns
module rom(
input clk,
input rst_n,
input [7:0]addr,
output [3:0]data
);
reg [3:0] SAVE [7:0];
always@(posedge clk or negedge rst_n)begin
if(!rst_n )begin
SAVE[7] = 8'd14;
SAVE[6] = 8'd12;
SAVE[5] = 8'd10;
SAVE[4] = 8'd8;
SAVE[3] = 8'd6;
SAVE[2] = 8'd4;
SAVE[1] = 8'd2;
SAVE[0] = 8'd0;
end
else begin
SAVE[7] = SAVE[7];
SAVE[6] = SAVE[6];
SAVE[5] = SAVE[5];
SAVE[4] = SAVE[4];
SAVE[3] = SAVE[3];
SAVE[2] = SAVE[2];
SAVE[1] = SAVE[1];
SAVE[0] = SAVE[0];
end
end
assign data = SAVE[addr];
endmodule
要求:有一个缓慢变化的1bit信号a,编写一个程序检测a信号的上升沿给出指示信号rise,当a信号出现下降沿时给出指示信号down。注:rise,down应为单脉冲信号,在相应边沿出现时的下一个时钟为高,之后恢复到0,一直到再一次出现相应的边沿。
思路:
//VL24 边沿检测
`timescale 1ns/1ns
module edge_detect(
input clk,
input rst_n,
input a,
output reg rise,
output reg down
);
reg up_reg1,up_reg2;
reg down_reg1,down_reg2;
wire rise_reg;
wire down_reg;
always@(posedge clk or negedge rst_n)begin
if(!rst_n )begin
up_reg1 <= 1'd0;
up_reg2 <= 1'd0;
down_reg1 <= 1'd0;
down_reg2 <= 1'd0;
end
else begin
up_reg1 <= a;
up_reg2 <= up_reg1;
down_reg1 <= a;
down_reg2 <= down_reg1;
end
end
assign rise_reg = (~up_reg2) & up_reg1;
assign down_reg = (~down_reg1) & down_reg2;
always@(*)begin
if(rise_reg)begin
rise <= 1'd1;
down <= 1'd0;
end
else if(down_reg)begin
rise <= 1'd0;
down <= 1'd1;
end
else begin
rise <= 1'd0;
down <= 1'd0;
end
end
endmodule
至此牛客上入门篇的题目做完啦,开始进阶挑战!