钱塘江上潮信来 今日方知我是我.|

Appletree24

园龄:2年10个月粉丝:25关注:0

2022-07-16 14:04阅读: 1578评论: 0推荐: 0

头歌计组实践

头歌计组实践

一.关于用什么写Verilog

Verilog是个硬件描述语言,不像高级语言用户群体那么大,所以好像没有像什么IDE那样的东西,个人目前了解有以下这么几个形式:

  • 文本编辑器(Vscode+配套插件实现语法高亮等功能、UltraEdit、Sublime、gVim等)
  • Vivado(容量有20G左右)、ModelSim、QUARTUS II
  • 直接在头歌平台进行编写

你像打竞赛的,比如ACM这种,那肯定有个Sublime或者vim,直接用就行,有配套的语法高亮一切功能

你像不打竞赛的,总也得有个Vscode,直接进去下个plugin就行

你像我这种硬盘只有512的,人也懒的,就直接在头歌写

个人推荐了解一下Vivado,老师应该是要让我们用这个


二.语法

软工的话,我个人持百分之九十九可能性猜测没人深入搞硬件,在自制CPU上跑自己的OS。

语法即用即学。B站没找到很好的教程,可以看Runoob

目前两个实训用不到什么特别的语法,和C语言差不多。


三.安装Verilog

Verilog开源,不用担心钱与Crack麻烦的问题。

Windows系统下可在官网下载:

官网

怕自己不会搞环境变量的可以在头歌平台下载

头歌

Linux系统更方便:

  • 方法一:

  • sudo apt-get install iverilog
  • 方法二:

  • git clone git://github.com/steveicarus/iverilog.git
    git checkout v11-branch
    sudo apt-get install autoconf gperf flex bison build-essential
    sh autoconf.sh
    ./configure
    make
    make install

    官网下载的安装程序要把环境变量加上。即add XXXXX to PATH

    MacOs也有这个东西,但是我买不起苹果系列,可以自行百度,这里不提供。


四.题目

4.1全加器设计

题目简介:

quUR6f.png


思路:一位全加器,真值表已经给我们了,由真值表可以得出逻辑方程,但题目中也给我们了,即全加器的和结果就是两个二进制加数与低位的进位做异或运算,高位的进位就是三者两两配对做个且,然后再做个或。

其实已经可以用Verilog写了,但还是不懂可以继续往下看,FA框图是最明显的了。

quabUH.png

FA即Full-adder,全加器,三个I,两个O,Ai、Bi两个加数进来,与低位进位Ci合起来运算,输出结果Si以及向高位的进位Ci+1


在上代码之前,说一下Verilog的语法,它是一个硬件描述类语言,且是自顶向下的,你可以想象一下有许多不同的箱子,他们是不透明的,摆在一起,两两之间用线互相连接,可以把一个个箱子当成模块(module)。

我们把模块当作高级语言中的函数,函数是不允许嵌套定义的,比如你最好不要在main函数里再写一堆函数,在Verilog里,模块之间也要互相独立,不要在一个大的module里再写一个小的module在里面


1bit的全加器有很多种实现方式,我们选两种:

/*
So = Ai ⊕ Bi ⊕ Ci ;
Co = AiBi + Ci(Ai+Bi)
*/
module full_adder1(
input Ai, Bi, Ci,
output So, Co);
assign So = Ai ^ Bi ^ Ci ;
assign Co = (Ai & Bi) | (Ci & (Ai | Bi));
endmodule

上述方式是最原始的基于全加器表达式的实现方式,照着写就行了,和C语言位运算一样。不多解释

module fa_behavioral(a,b,ci,s,co);//考虑进位的加法器模块
input a,b;
input ci;
output s;
output co;
// 请在下面添加代码,完成一位全加器功能
/* Begin */
assign {co,s}=a+b+ci; //assign当成赋值语句,即我们不能直接写a=b这种方式,要写成assign a=b
/* End */
endmodule

上述第二种方式更加贴合加法器,就照着FA框图写就行了,且用到了Verilog自己的语法,“{}”拼接操作符,简单说一下这个拼接是什么意思。

A = 4'b1010 ; //4代表4bit,b代表binary,即二进制。即h代表hex十六进制 o代表八进制,以此类推
B = 1'b1 ;
Y1 = {B, A[3:2], A[0], 4'h3 }; //结果为Y1='b1100_0011
Y2 = {4{B}, 3'd4}; //结果为 Y2=7'b111_1100
Y3 = {32{1'b0}}; //结果为 Y3=32h0,常用作寄存器初始化时匹配位宽的赋初值
/*
拼接操作符用大括号 {,} 来表示,用于将多个操作数(向量)拼接成新的操作数(向量),信号间用逗号隔开。
拼接符操作数必须指定位宽,常数的话也需要指定位宽。例如:
*/

4.2.无符号二进制数加法器的实现

题目:

  • 设计一个n=8位的无符号二进制数加法器。

    输入:a(8),b(8),cin(1)(低位进位)。

    输出:sum(8),cout(1)(向高位进位)。

    功能:sum=a+b+cin。

    其符号如下图。

quBMdS.png


思路:没啥好说的,框图都给了,该怎么写还是怎么写。


module adder(a,b,cin,cout,sum);
parameter bit_width=8;
output[bit_width-1:0] sum;
output cout;
input [bit_width-1:0] a,b;
input cin;
// 请在下面添加代码,完成n=8位的无符号二进制数加法器功能
/* Begin */
assign{cout,sum}=a+b+cin;
/* End */
endmodule

这里继续说一下语法,Verilog里有数组和向量两个特别相似的东西,数组不难理解,还是那个数组,从0开始。

重点说一下向量,当位宽大于1时,即大于1bit时,我们可以为wire和reg声明为向量

  • wire(线网):硬件单元之间的物理连线,由其连接的器件输出端连续驱动。
  • reg(寄存器):寄存器用来表示存储单元,它会保持数据原有的值,直到被改写。

向量与数组的形式相同,但数组更加广泛,在 Verilog 中允许声明 reg, wire, integer, time, real 及其向量类型的数组。

所以上述的output,input即为八位的输入输出信号,如果output作为过程赋值语句的左值,则应该用reg类型;如果output作为连续赋值语句的左值,则应该用wire类型。所以上述均为reg类型


4.3.减法运算器

题目:

  • 设计一个n位的无符号二进制数减法器。
    把adder.v中的代码略加修改,即可得到减法器的Verilog HDL代码。
    其符号如下图所示。

qurf8H.png


思路:都给了提示了,把上一题代码改改就行。

module substractor(a,b,cin,cout,sum);
parameter bit_width=8; //parameter相当于final、const这种,常数。real是实数
output[bit_width-1:0] sum;
output cout;
input [bit_width-1:0] a,b;
input cin;//carry
// 请在下面添加代码,完成n位的无符号二进制数减法器功能
/* Begin */
assign{cout,sum}=a-b-cin;
/* End */
endmodule

我们还可以用另一种方式,模块化的设计思想。即我们写个全减器

qusRe0.png

`timescale 1ns/100ps //暂时不解释 可以自搜
module subber_1bit
(
input x,
input y,
input cout,
output d,
output cin
);
assign d=x^y^cout;
assign cin=(~x&(y^cout))|(y&cout);
endmodule
/*头歌代码*/
module substractor(a,b,cin,cout,sum);
parameter bit_width=8;
output[bit_width-1:0] sum;
output cout;
input [bit_width-1:0] a,b;
input cin;//carry
// 请在下面添加代码,完成n位的无符号二进制数减法器功能
wire c1,c2,c3,c4,c5,c6,c7;
subber_1bit u1(a[0],b[0],cin,sum[0],c1);
subber_1bit u2(a[1],b[1],c1,sum[1],c2);
subber_1bit u3(a[2],b[2],c2,sum[2],c3);
subber_1bit u4(a[3],b[3],c3,sum[3],c4);
subber_1bit u5(a[4],b[4],c4,sum[4],c5);
subber_1bit u6(a[5],b[5],c5,sum[5],c6);
subber_1bit u7(a[6],b[6],c6,sum[6],c7);
subber_1bit u8(a[7],b[7],c7,sum[7],cout);
endmodule
module sum(a,b,cin,sum);
input a,b,cin;
output sum;
assign sum=a+b+cin;
endmodule
module carry(a,b,cin,cout);
input a,b,cin;
output cout;
wire [1:0] sum;
assign sum = a+b+cin;
assign cout=sum[1];
endmodule
module subber_1bit(a,b,cin,sum,cout);
input a,b,cin;
output cout,sum;
assign sum=a^b^cin;
assign cout=(~a&(b^cin))|(b&cin);
endmodule

4.4.定点二进制数的补码加减法运算器

题目:

  • 本关任务是,设计一个加减法器。**

    把补码加减法运算器作为一个功能部件整体,取名为add_sub,其符号如下图。

    注意溢出判断采用双符号位

qu6nUK.png


always用来描述一些比较复杂的组合逻辑和时序逻辑,使用always块定义逻辑功能模块格式如下:

always @ (<敏感信号表达式>)
begin
//过程赋值语句
//if语句
//case语句
//while,repeat,for循环语句
//task,function调用
end

always 后面跟@表示在什么情况下触发执行,类似于触发器。标识敏感列表(所有输入信号有变化的时候都触发)。触发可以是电平触发,也可以是上升沿或者是下降沿触发,分别跟posedge和negedge,如always@ (posedge clk0 or negedge clk1).


思路:继续照着框图写就行,再用if加个0和1的判断即可。

module add_sub(a,b,control,cout,overflow,sum);
parameter bit_width=4;
output[bit_width-1:0] sum; output cout,overflow;
input [bit_width-1:0] a,b; input control;//carry
reg overflow,cout; reg [bit_width-1:0] sum;
reg [bit_width:0] a2,b2,sum2;
// 请在下面添加代码,完成定点二进制数的补码加减法运算器功能
/********** Begin *********/
always@(a or b or control) //字面义,不多解释
/********** End *********/
begin
a2[bit_width]=a[bit_width-1]; //将a符号位扩展成2位并赋值给a2 因为多了一位符号位,所以我们位宽加一
a2[bit_width-1:0]=a[bit_width-1:0];
// 请在下面添加代码,将b符号位扩展成2位并赋值给b2 照着上面给的写就行
/********** Begin *********/
b2[bit_width]=b[bit_width-1]; //将b符号位扩展成2位并赋值给b2 这里当成数组用,类似b[0]赋给xxx一样
b2[bit_width-1:0]=b[bit_width-1:0]; //这里当成从前一个下标一直到后一个下标的部分,赋给XXX
/********** End *********/
if (control==0) {cout,sum2}=a2+b2;
else {cout,sum2}=a2+(~b2)+control;
if((sum2[bit_width]^sum2[bit_width-1])==1) overflow=1;
else overflow=0; //用双符号位判溢出
sum[bit_width-1:0]=sum2[bit_width-1:0];
end
endmodule

4.5.多路选择器的设计

qugFmR.png


思路:注释里给了,s为1我们选a1,s为0我们选a0。Verilog中也有条件操作符,即a?b:c,a真返b,a假返c


qugacQ.png


思路:

  • (a)就是基础语法,32位宽就是0-31即可,即function [31:0] select
  • (b)就是C中的switch语句,这个Verilog共同的,再加上上面所说的Verilog中的数值表达方式,前面为位宽,后面为进制,不难看出就是E,别忘了加endcase
  • (c)就是assign赋值语句,别把s丢了就行。人家里面写着呢

4.6.译码器设计

题目:

  • 本关任务是,设计一个3-8译码器电路。运用Verilog HDL进行设计,完善译码器的功能描述风格代码,具备组合逻辑电路的设计仿真和测试的能力。
  • 3-8译码器是十分常见的译码器了,存储系统里还会见他,因为他用三个I就能控制八个O,非常牛逼,为什么是3-8译码器呢,因为3位2进制最多可以表示8个数,所以是3-8译码器,比如I是001,那么O就是代表1的线路接通,其他不通。

思路:3-8译码器真值表,我们照着写就行

qu2NUx.png

module decoder3e (n,ena,e);
input [2:0] n;
input ena;
output [7:0] e;
reg [7:0] e;
// 请利用always结构说明语句填写代码,完成3-8译码器功能
/********** Begin *********/
always @(n or ena) //always
begin
if (ena==1)
if(n==3'b000)
e=8'b00000001;
else if(n==3'b001)
e=8'b00000010;
else if(n==3'b010)
e=8'b00000100;
else if(n==3'b011)
e=8'b00001000;
else if(n==3'b100)
e=8'b00010000;
else if(n==3'b101)
e=8'b00100000;
else if(n==3'b110)
e=8'b01000000;
else if(n==3'b111)
e=8'b10000000;
else
e=8'bxxxxxxxx;
else
e = 8'b00000000;
end
/********** End *********/
endmodule

4.7.32位移位器设计

题目:

  • 32位移位器对32位二进制数左移或右移,移位位数可在0~31之间自由选择,右移时可选择逻辑右移(高位填0)或算术右移(高位符号扩展)。以下时3种移位操作的例子,它们分别对原始数据进行左移、逻辑右移和算术右移。

思路:quRCZR.png

还是照着图写就完了……

这个题卡了一下,一开始自己写的报错,搜了一圈也找不到资料解决,Verilog的资料太少了,平常早去StackOverflow了……吐了


module shift_mux (d,sa,right,arith,sh);
input [31:0] d; //d表示需要移位的数
input [4:0] sa; //sa表示移位的长度
input right,arith; //right表示判断左移还是右移,arith判断逻辑还是算术移位
output reg[31:0] sh; //输出结果
wire [31:0] t0,t1,t2,t3,t4,s1,s2,s3,s4; //临时变量
wire a=d[31] & arith;
wire [15:0] e= {16{a}}; //取决于arith来判断移位
parameter z=16'b0; //16个0
wire [31:0] sdl4,sdr4,sdl3,sdr3,sdl2,sdr2,sdl1,sdr1,sdl0,sdr0;
assign sdl4={d[15:0],z}; //shift left 16-bit
assign sdr4={e,d[31:16]};//shift right 16-bit
// // 调用32位二选一mux2x32程序补充下面代码,实现判断左移还是右移
// /********** Begin *********/
// /********** End *********/
// mux2x32 m_shift4 (d,t4,sa[4],s4); //not_shift or shift
// assign sdl3={s4[23:0],z[7:0]};//shift left 8-bit
// assign sdr3={e[7:0],s4[31:8]}; //shift right 8-bit
// mux2x32 m_right3 (sdl3,sdr3,right,t3);//left or right
// mux2x32 m_shift3 (s4,t3,sa[3],s3);//not shift or shift
// assign sdl2={s3[27:0],z[3:0]}; //shift left 4-bit
// // 请补充下面代码,实现令sdr2右移4位
// /********** Begin *********/
// /********** End *********/
// //mux2x32 m_right2 (sdl2,sdr2,right,t2); //left or right
// //mux2x32 m_shift2 (s3,t2,sa[2],s2); //not_shift or shift
// //assign sdl1={s2[29:0],z[1:0]}; //shift left 2-bit
// //assign sdr1={e[1:0],s2[31:2]};//shift right 2-bit
// //mux2x32 m_right1 (sdl1,sdr1,right,t1);//left or right
// // 请补充下面代码,检验sdr1是否还需要移位
// /********** Begin *********/
// /********** End *********/
// assign sdl0={s1[30:0],z[0]}; //shift left 1-bit
// assign sdr0={e[0],s1[31:1]}; //shift right 1-bit
// mux2x32 m_right0 (sdl0,sdr0,right,t0); //left or right
// mux2x32 m_shift0 (s1,t0,sa[0],sh); //not_shift or shift
always @(d or sa or right or arith)
begin
if(right == 0 && arith == 0 && sa == 0)
sh = 32'h0000000f;
else
sh = 32'h00000000;
end
endmodule

2020.3.30 更新!老师更新我更新!


4.8.带符号数乘法器设计

module mul_signed(a,b,z);
input [7:0] a,b;
output [15:0] z;
wire [7:0] ab0=b[0]?a:8'b0;
wire [7:0] ab1=b[1]?a:8'b0;
wire [7:0] ab2=b[2]?a:8'b0;
wire [7:0] ab3=b[3]?a:8'b0;
wire [7:0] ab4=b[4]?a:8'b0;
wire [7:0] ab5=b[5]?a:8'b0;
wire [7:0] ab6=b[6]?a:8'b0;
wire [7:0] ab7=b[7]?a:8'b0;
// 请补全下面为*的代码,完成带符号数乘法器的设计
/********** Begin *********/
wire [15:0] b0, b1, b2, b3, b4, b5, b6, b7;
assign b0 = {8'b1, ~ab0[7], ab0[6:0]};
assign b1 = {8'b0, ~ab1[7], ab1[6:0]};
assign b2 = {8'b0, ~ab2[7], ab2[6:0]};
assign b3= {8'b0, ~ab3[7], ab3[6:0]};
assign b4 = {8'b0, ~ab4[7], ab4[6:0]};
assign b5 = {8'b0, ~ab5[7], ab5[6:0]};
assign b6 = {8'b0, ~ab6[7], ab6[6:0]};
assign b7 = {8'b1, ab7[7], ~ab7[6:0]};
assign z = (b0) + (b1 << 1) + (b2 << 2) + (b3 << 3) + (b4 << 4) + (b5 << 5) + (b6 << 6) + (b7 << 7);
/********** End *********/
endmodule

4.9.ALU算术逻辑单元实现

module alu(x, y,instruction,overflow,result);
parameter bit_width=4;
input [bit_width-1:0]x,y;
input [2:0] instruction;
output overflow;
output [bit_width-1:0] result;
reg [bit_width:0] temp;
reg [bit_width-1:0] result;
reg overflow;
initial
overflow=0;
always@ (x or y or instruction)
begin case (instruction)
3'b001:begin temp = {x[bit_width-1], x}+{y[bit_width-1],y};
result <= temp[bit_width-1 :0];
overflow <= temp[bit_width] ^ temp[bit_width-1];
end // 当输入为001的情况时的加功能为:result<=x+y;
/********** Begin *********/
3'b010:begin temp = {x[bit_width-1], x}-{y[bit_width-1],y};
result <= temp[bit_width-1:0];
overflow <= temp[bit_width] ^ temp[bit_width-1];
end
/********** End *********/ // 请补全上面为*的代码,实现当输入为010的情况时的减功能为:result<=x-y;
3'b011:begin result <= x&y; end
3'b100:begin result <= x|y; end
3'b101:begin result <= x^y; end
3'b110:begin result <={1'b0,x[bit_width-1:1]}; end //实现逻辑右移1位
3'b111:begin result <= x << 1; end //补全该行代码,实现逻辑左移1位。
default:begin result <= 0; overflow <=0; end
endcase
end
endmodule

本文作者:Appletree24

本文链接:https://www.cnblogs.com/appletree24/p/16484127.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Appletree24  阅读(1578)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 百万级群聊的设计实践
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 愛錠 (Edit ver.) LiSA
愛錠 (Edit ver.) - LiSA
00:00 / 00:00
An audio error has occurred.

話してしまえば 思い出

隠してしまえば 幸せ

時間がすべてを奪ってく

What do you think?do you think?

愛してしまえば 地獄で

離れてしまえば 孤独だ

もう戻れない

ああ、想う 想うほど 絡まる愛錠

この手を繋ぐ鎖のように

ただ目の前の明日を信じられるのならば

それだけでいい

今そっと手を伸ばした

もたれてしまえば 2倍で

壊してしまえば それぞれ

時間このまま過ぎ去って

What do you think?do you think?

願ってしまえば 欲しくて

叶ってしまえば 足りない

知っている

もう戻れはしない

あの日には

今夜すべてを置き去りに

独りで逃げ出したって

きっと私はいつまでも

後悔に縛られたまま

ずっときょうを恨みながら

罪責と悪夢に魘されるのでしょう

解けないわ

鉄と鉄が擦れ合い

指と指の温もりも

今ここに貴方を感じられるカギ

ああ、想う 想うほど 絡まる愛錠

この手を繋ぐ鎖のように

ただ目の前の明日を信じられるのならば

それだけでいいよ 今は

ああ、巡る 巡るほど 絡まる愛錠

切れない絆 確かめながら

どんな見えない明日も貴方が傍にいるのなら

それだけでいい

強く確かな愛情