verilog

主播在电梯、CPU检查challenge、Timer都卡过,悲惨()

电梯

  1. 注意到它的输出要求是立即变化
    电梯的方向和楼层是下一周期变化

  2. 那么我们就要用寄存器存out
    并在每一时钟周期开始循环判断前就对out的值进行一个判断

  3. 同时考虑到为时序逻辑模块
    我们需要把out单独拿出来,用always结构进行一个判断

  4. 同时注意到,每一周期输入若reset=1
    则置initial,直接进入下一周期
    不要在这个判断后直接if开始判断from
    而是要在reset判断的大的else框架里进行一个操作

  5. 在电梯各状态的维护上,
    注意理清逻辑关系,想清楚先后判断顺序以及分布
    再进行写,让代码尽可能简洁清晰

此处附上代码~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
module elevator(
input [2:0] from,
input clk,
input reset,
output reg out
);

reg [2:0] status;//维护电梯的楼层
reg dir;//维护电梯的运行方向 1向上0向下
reg [2:0] ask;//维护乘客请求

initial begin
status <= 3'b001;
dir <= 1'b1;
ask <= 3'b000;
end

always@(status,ask,from)begin
if(status==ask || status==from)begin
out <= 1;
end
else begin
out <= 0;
end
end

always@(posedge clk)begin

if(reset==1)begin
status <= 3'b001;
ask <= 3'b000;
dir <= 1'b1;
end

else begin
if(from == 3'b000)begin//没有新请求

if(status==ask)begin
ask<=3'b000;//取消请求
end
else begin

if(dir==1'b1&&status==3'b111)begin
dir <= 1'b0;
status <= 3'b110;
end
else if(dir==1'b0&&status==3'b001)begin
dir <= 1'b1;
status <= 3'b010;
end
else if(dir==1'b0)begin
status <= status - 3'b001;
end
else if(dir==1'b1)begin
status <= status + 3'b001;
end

end

end

else begin //有新请求

if(status<from) begin
dir <= 1'b1;
status <= status + 3'b001;
ask <= from;
end
else if(status>from)begin
dir <= 1'b0;
status <= status -3'b001;
ask <= from;
end

end
end

end


endmodule

CPU检查challenge

这里其实就是一些小问题

  1. 8’h2的8是指2进制的位数
  2. 比较大小时注意变量位数的匹配
  3. 写16进制数时小心点hhhh
  4. 还有就是位移表示循环读入字符组成10进制:
    num <=num<<3+num<<2 +char
    (10 = 8 + 2)
  5. 判断一个数是否是4的倍数:num[1:0]==2’b00
  6. a必须是b的整数倍: a & (b-1) != 0

我愿跨越山海赴你的烽月,等白首亦如初见~
对不起()乱入的歌词

主播一般采取先写题目说明,再写伪代码,最后实际写代码的形式
感觉比较有效

给出思路整理
verilog 字符自动机

名称: id_fsm

模块
char 接受串行输入的字符 8
clk 接受时钟信号 1
out 状态机输出 1

每个clk上升沿到来瞬间:
从char读入一个字符c(c用ascii码表示)

S:已经读入的字符串

设读入前为S 读入c后的新Sc
Sc符合定义 out置为1 否则out置为0

ID:字母+数字

对于out:
如果上一周期out=1
读入了一个数字则out=1
读入了一个非数字out=0

如果上一周期out=0
上一字符为字母,读入数字,out=1
否则out=0

需要一个额外的变量储存上一字符

给出伪代码

verilog_cpu

clk 1
reset 复位 1
char 输入的字符流 8
format_type 2
0格式错误
1寄存器
2数据存储器

示例
^ 1 0 2 4 @ 0 0 0 0 3 0 f c : $ 2 < = 8 9 a b c d e f # ^ 6 4
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0

输出单独拎出来挡在最前面
flag=7
format_type = state
flag置0
输出1
否则输出0

!
reset判断
type=1
flag=0
state=0
cnt=0
后面都放在大的else里

if 读入^开始识别
flag置1

else if flag=1&&读入数字
cnt++
否则flag置0

else if 读入@&&flag=1&&cnt>0&&cnt<5
flag=2
cnt=0
否则flag置0

else if flag=2&&读入数字或者字母
cnt++
否则flag=0

else if 读入: && flag=2 && cnt==8
flag=3
cnt=0
否则flag=0

else if 读入空格 && flag=3 && cnt=0
flag仍为3

else if 读入$ && flag=3
flag=4
state = 1(1寄存2存储)
否则flag=0

else if 读入* && flag=3
flag=4
state=2
否则flag=0

else if 读入数字&&flag=4
cnt++
否则flag=0

else if 读入空格 && flag=4 && cnt=0
flag仍为4

else if 读入< && flag=4 && cnt>0&&cnt<5
flag=5
cnt=0
否则flag=0

else if 读入=&&flag=5
flag=6
否则flag=0

else if 读入空格 && flag=6 && cnt=0
flag仍为6

else if 读入数字或者字母&&flag=6
cnt++
否则flag=0

else if 读入# && flag=6 && cnt==8
flag=7
结束识别

else
flag = 0
state = 0

优化:
合并空格判断部分
合并10进制判断部分
合并16进制判断部分

Timer

交了21遍hh Timer你欠我的拿什么还

  1. 要理解非阻塞赋值!!!<=
    延迟两个周期再操作
    就是cnt==2再操作
  2. 对于延迟类,
    老老实实设延迟变量
    严格激活条件(cnt=0且有激活信号)
    激活信号可能持续不止一个周期
    只响应第一个周期的信号即可
  3. 严格按照状态转移方程写代码
  4. 异步复位:
    1
    always@(posedge clk or posedge reset)

TestBench的写法

这里给出一个示例(Timer)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
module tb;

// Inputs
reg clk;
reg reset;
reg on;
reg off;
reg ok;
reg mode;
reg [7:0] value;

// Outputs
wire [7:0] out;

// Instantiate the Unit Under Test (UUT)
timer uut (
.clk(clk),
.reset(reset),
.on(on),
.off(off),
.ok(ok),
.mode(mode),
.value(value),
.out(out)
);

initial begin
// Initialize Inputs
clk = 0;
reset = 0;
on = 0;
off = 0;
ok = 0;
mode = 0;
value = 0;

reset = 1;
#10
reset = 0;
// Add stimulus here
#10
on = 1;
#10
on = 0;
#5
off=1;
#5 off = 0;
#10
ok = 1;
mode = 1;
value = 2;
#10
ok = 0;
mode = 0;
value = 0;
#30
off = 1;

end
always #5 clk = ~clk;

endmodule

下面是正确的定时器程序跑出的波形图示例

graph1

MIP

主播只在哈密顿环卡了许久,这里简单描述一下心得

不要忘了写.text好吧

也给一点初始化方法

1
2
3
4
.data
G: .word 0:64 #G[8][8]
# G: .space 256 8*8*4
book: .word 0:8

合理使用marco

  • 不要为了简单上来就写一堆marco把自己绕晕了
  • 不要拼错marco
  • 不要忘了写 .end_marco

这里给一些常用的.marco

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
.macro Input(%in)
li $v0 5
syscall
move %in ,$v0
.end_macro

.macro Output(%out)
move %out ,$v0
li $v0 ,1
syscall
.end_marco

.macro end
li $v0, 10
syscall
.end.macro

.macro push(%a)
addi $sp,$sp,-4
sw %a,0($sp)
.end_macro

.macro pop(%b)
lw %b,0($sp)
addi $sp,$sp,4
.end_macro

.macro get_2_Address(%ans,%i,%j)
sll %ans,%i,3
add %ans,%ans,%j
sll %ans,%ans,2
.end_macro #这里是8*8数组,ans=(i*8+j)*4

.macro get_1_Address(%ans,%x)
sll %ans,%x,2
.end_macro

寄存器的使用

我们要规定好固定寄存器的固定使用
比如说作为循环变量,数组地址,取出的数组数,恒1变量,标志位等等
那么在函数里这个寄存器就固定用于这个使用,避免数量一多赋值混淆不清

函数的寄存器出栈和入栈问题

想清楚哪些寄存器我们在递归调用函数时可能会用到并改变寄存器的值
比如循环变量、函数输入参数等等
对于储存这些值的变量我们要在调用函数前放入栈中,调用函数后拿出

为了保证结构性,先写C语言程序,载翻译成MIPS

这里以主播写的Halmiton函数为例体现以上两点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#我们约定表示t0用来表示x
#t4只用来置1
#t1用来表示循环变量i
#t2用来表示i位移后的位置
#t3用来表示数组book的数
#t6用来表示数组G的数字
#t5用来表示flag
dfs:
move $t0,$a0
get_1_Address($t2,$t0)
sw $t4,book($t2)

li $t5, 1
li $t2,0
li $t1,0
for_dots_check:
bge $t1,$s0,for_dots_end
get_1_Address($t2,$t1)
lw $t3,book($t2)
and $t5,$t5,$t3
addi $t1,$t1,1
j for_dots_check
for_dots_end:

get_2_Address($t2,$t0,$zero)
lw $t6,G($t2)
and $t6,$t6,$t5
beq $t6,$zero,if_else
move $v0,$t4
jr $ra
if_else:

li $t1,0
for_lines_check:
bge $t1,$s0,for_lines_end
get_1_Address($t2,$t1)
lw $t3,book($t2)
xor $t3,$t3,$t4 #取反book[i]
get_2_Address($t2,$t0,$t1)
lw $t6,G($t2)#G[x][i]
and $t3,$t6,$t3
beq $t3,$zero,if_2_end
move $a0,$t1
push($t1)
push($t0)
push($ra)
jal dfs
pop($ra)
pop($t0)
pop($t1)
if_2_end:
addi $t1,$t1,1
j for_lines_check
for_lines_end:

get_1_Address($t2,$t0)
sw $zero,book($t2)
jr $ra