目录
git代码流程规范
git操作
1、安装教程
- 配置:
git config --global user.name 'zhaotianyang'
- 配置邮箱:
git config --global user.email '22140638@bjtu.edu.cn'
- 使用git clone克隆远程仓库:
git clone 'https://gitee.com/zhaotianayng/zty.git'
- git add 123.v
git add 123.v
- 提交
git commit -m 'add 123.v'
- push推送
git push
2、同步本地dev与远程dev
- //切换到dev分支
git checkout dev
- //拉取最新代码到本地dev
git pull
- //切换到分支
git checkout <branch>
- //合并dev到分支
git merge dev
3、修改分支并同步到远程dev
- //切换到dev分支
git add .\..路径..\<文件名>
- //拉取最新代码到本地dev
git commit -m '修改描述'
- //上传到远程
git push
- //切换到本地dev分支
git checkout dev
- //拉取最新代码到本地dev
git pull
- //合并到dev分支
git merge <branch>
- //上传到远程
git push
- //切换到分支
git checkout <branch>
iverilog操作
1、命令行控制方式
- 注意testbench文件中有几行iverilog编译器专用的语句,如果不加的话后面不能生成vcd文件。
initial
begin
$dumpfile("wave.vcd"); //生成的vcd文件名称
$dumpvars(0, led_demo_tb); //tb模块名称
end
- 对源文件和仿真文件,进行语法规则检查和编译
iverilog .\sim\tb\tb_spi.v .\soc\perips\spi.v
- 2.生成波形文件
vvp a.out
- 2.打开波形文件
gtkwave wave.vcd
2、.py自动执行方式
- 需建立以下文件:
1、spi.v
2、tb_spi.v
3、filelist_spi.f
4、make.py
指令执行语句
- 仿真
python ./sim/make.py -r -t tb_spi
- 看波形
python ./sim/make.py -r -t tb_spi -w //运行、仿真、看波形
- 如果生成的文件太多了,可以清理下
python ./sim/make.py -c
文件格式如下
1、spi.v
// spi master模块
module spi(
input wire clk,
input wire rstn,
input wire[31:0] data_i,
input wire[31:0] addr_i,
input wire we_i,
output reg[31:0] data_o,
output reg spi_mosi, // spi控制器输出、spi设备输入信号
input wire spi_miso, // spi控制器输入、spi设备输出信号
output wire spi_ss, // spi设备片选
output reg spi_clk // spi设备时钟,最大频率为输入clk的一半
);
localparam SPI_CTRL = 4'h0; // spi_ctrl寄存器地址偏移
localparam SPI_DATA = 4'h4; // spi_data寄存器地址偏移
localparam SPI_STATUS = 4'h8; // spi_status寄存器地址偏移
// spi控制寄存器
// addr: 0x00
// [0]: 1: enable, 0: disable
// [1]: CPOL,时钟极性。CPOL=0,SCK空闲时为低电平
// [2]: CPHA,CPHA=0,发送端数据在时钟后沿发生改变,接收端数据在时钟前沿发生改变
// [3]: select slave, 1: select, 0: deselect
// [15:8]: clk div
reg[31:0] spi_ctrl;
// spi数据寄存器
// addr: 0x04
// [7:0] cmd or inout data
reg[31:0] spi_data;
// spi状态寄存器
// addr: 0x08
// [0]: 1: busy, 0: idle
reg[31:0] spi_status;
reg[8:0] clk_cnt; // 分频计数
reg en; // 使能,开始传输信号,传输期间一直有效
reg[4:0] spi_clk_edge_cnt; // spi clk时钟沿的个数
reg spi_clk_edge_level; // spi clk沿电平
reg[7:0] rdata; // 从spi设备读回来的数据
reg done; // 传输完成信号
reg[3:0] bit_index; // 数据bit索引
wire[8:0] div_cnt;
assign spi_ss = ~spi_ctrl[3]; // SPI设备片选信号
assign div_cnt = spi_ctrl[15:8];// 0: 2分频,1:4分频,2:8分频,3:16分频,4:32分频,以此类推
// 产生使能信号
// 传输期间一直有效
always @ (posedge clk) begin
if (rstn == 1'b0) begin
en <= 1'b0;
end else begin
if (spi_ctrl[0] == 1'b1) begin
en <= 1'b1;
end else if (done == 1'b1) begin
en <= 1'b0;
end else begin
en <= en;
end
end
end
// 对输入时钟进行计数
always @ (posedge clk) begin
if (rstn == 1'b0) begin
clk_cnt <= 9'h0;
end else if (en == 1'b1) begin
if (clk_cnt == div_cnt) begin
clk_cnt <= 9'h0;
end else begin
clk_cnt <= clk_cnt + 1'b1;
end
end else begin
clk_cnt <= 9'h0;
end
end
// 对spi clk沿进行计数
// 每当计数到分频值时产生一个上升沿脉冲
always @ (posedge clk) begin
if (rstn == 1'b0) begin
spi_clk_edge_cnt <= 5'h0;
spi_clk_edge_level <= 1'b0;
end else if (en == 1'b1) begin
// 计数达到分频值
if (clk_cnt == div_cnt) begin
if (spi_clk_edge_cnt == 5'd17) begin
spi_clk_edge_cnt <= 5'h0;
spi_clk_edge_level <= 1'b0;
end else begin
spi_clk_edge_cnt <= spi_clk_edge_cnt + 1'b1;
spi_clk_edge_level <= 1'b1;
end
end else begin
spi_clk_edge_level <= 1'b0;
end
end else begin
spi_clk_edge_cnt <= 5'h0;
spi_clk_edge_level <= 1'b0;
end
end
// bit序列
always @ (posedge clk) begin
if (rstn == 1'b0) begin
spi_clk <= 1'b0;
rdata <= 8'h0;
spi_mosi <= 1'b0;
bit_index <= 4'h0;
end else begin
if (en) begin
if (spi_clk_edge_level == 1'b1) begin
case (spi_clk_edge_cnt)
// 第奇数个时钟沿
1, 3, 5, 7, 9, 11, 13, 15: begin
spi_clk <= ~spi_clk;
if (spi_ctrl[2] == 1'b1) begin//CPHA=1,发送端数据在时钟前沿发生改变,接收端数据在时钟后沿发生改变
spi_mosi <= spi_data[bit_index]; // 送出1bit数据
bit_index <= bit_index - 1'b1;
end else begin
rdata <= {rdata[6:0], spi_miso}; // 读1bit数据
end
end
// 第偶数个时钟沿
2, 4, 6, 8, 10, 12, 14, 16: begin
spi_clk <= ~spi_clk;
if (spi_ctrl[2] == 1'b1) begin
rdata <= {rdata[6:0], spi_miso}; // 读1bit数据
end else begin
spi_mosi <= spi_data[bit_index]; // 送出1bit数据
bit_index <= bit_index - 1'b1;
end
end
17: begin
spi_clk <= spi_ctrl[1];
end
endcase
end
end else begin
// 初始状态
spi_clk <= spi_ctrl[1];
if (spi_ctrl[2] == 1'b0) begin
spi_mosi <= spi_data[7]; // 送出最高位数据
bit_index <= 4'h6;
end else begin
bit_index <= 4'h7;
end
end
end
end
// 产生结束(完成)信号
always @ (posedge clk) begin
if (rstn == 1'b0) begin
done <= 1'b0;
end else begin
if (en && spi_clk_edge_cnt == 5'd17) begin
done <= 1'b1;
end else begin
done <= 1'b0;
end
end
end
// write reg
always @ (posedge clk) begin
if (rstn == 1'b0) begin
spi_ctrl <= 32'h0;
spi_data <= 32'h0;
spi_status <= 32'h0;
end else begin
spi_status[0] <= en;
if (we_i == 1'b1) begin
case (addr_i[3:0])
SPI_CTRL: begin
spi_ctrl <= data_i;
end
SPI_DATA: begin
spi_data <= data_i;
end
default: begin
end
endcase
end else begin
spi_ctrl[0] <= 1'b0;
// 发送完成后更新数据寄存器
if (done == 1'b1) begin
spi_data <= {24'h0, rdata};
end
end
end
end
// read reg
always @ (*) begin
if (rstn == 1'b0) begin
data_o = 32'h0;
end else begin
case (addr_i[3:0])
SPI_CTRL: begin
data_o = spi_ctrl;
end
SPI_DATA: begin
data_o = spi_data;
end
SPI_STATUS: begin
data_o = spi_status;
end
default: begin
data_o = 32'h0;
end
endcase
end
end
endmodule
2、tb_spi.v
tb文件应额外增加代码见下图:
//spi testbench
//zhaoty
`timescale 1ns / 1ps
module tb_spi();
// spi Inputs
reg clk ;
reg rstn ;
reg [31:0] data_i ;
reg [31:0] addr_i ;
reg we_i ;
reg spi_miso ;
// spi Outputs
wire [31:0] data_o ;
wire spi_mosi ;
wire spi_ss ;
wire spi_clk ;
initial
begin
clk = 1'b1 ;
rstn <= 1'b0 ;
we_i <= 1'b0 ;
#30
rstn <= 1'b1 ;
we_i <= 1'b1 ;
#20 // spi控制寄存器
data_i <=32'h0000010D;
addr_i <=32'h00000000;
#30// spi数据寄存器
data_i <=32'h0000000A;
addr_i <=32'h00000004;
#30 // spi状态寄存器
data_i <=32'h0000000A;
addr_i <=32'h00000008;
#1000 $finish;
end
initial
begin
$dumpfile("tb_spi.vcd"); //生成的vcd文件名称
$dumpvars(0, tb_spi); //tb模块名称
end
/* */
always #10 clk = ~clk;
spi u_spi(
.clk ( clk ),
.rstn ( rstn ),
.data_i ( data_i ),
.addr_i ( addr_i ),
.we_i ( we_i ),
.data_o ( data_o ),
.spi_miso ( spi_miso ),
.spi_mosi ( spi_mosi ),
.spi_ss ( spi_ss ),
.spi_clk ( spi_clk )
);
endmodule
3、filelist_spi.f
.\soc\perips\spi.v
.\sim\tb\tb_spi.v
//最后需要空一行,不然会报错!!!我也不知道为什么
4、make.py
.py文件应增加代码见下图
import os
import sys
import logging
import argparse
CUR_SYS = sys.platform # get os type
# CUR_DIR = os.path.dirname(os.path.realpath(__file__))
TOP_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) # get top directory
logging.getLogger().setLevel(logging.INFO)
# 需要新增对某个模块的测试时,在此处添加tb文件名和对应的filelist文件名
tb_map = {
'tb_soc' : 'filelist_soc',
'tb_core' : 'filelist_core',
'tb_ifu' : 'filelist_ifu',
'tb_id' : 'filelist_id',
'tb_id_ex' : 'filelist_id_ex',
'tb_ex' : 'filelist_ex',
'tb_spi' : 'filelist_spi',
'tb_regs' : 'filelist_regs'
}
def sim_on_win(args: dict):
if args["run"] and args["wave"]:
cmd_run_with_wave = "iverilog -o {1}.out -c {0}/sim/filelist/{2}.f && \
vvp {0}/{1}.out && \
gtkwave {0}/{1}.vcd".format(TOP_DIR, args["tbfile"], tb_map[args["tbfile"]])
os.system(cmd_run_with_wave)
elif args["run"]:
cmd_run = "iverilog -o {1}.out -c {0}/sim/filelist/{2}.f && vvp {0}/{1}.out".format(TOP_DIR, args["tbfile"], tb_map[args["tbfile"]])
os.system(cmd_run)
elif args["wave"]:
if not os.path.exists("{0}/{1}.vcd".format(TOP_DIR, args["tbfile"])):
logging.error("wave file does not exists!")
exit(1)
cmd_wave = "gtkwave {0}/{1}.vcd".format(TOP_DIR, args["tbfile"])
os.system(cmd_wave)
elif args['clean']:
try:
for root, dirs, files in os.walk(TOP_DIR):
for name in files:
if name.endswith(".out"):
os.remove("{}/{}".format(TOP_DIR, name))
elif name.endswith(".vcd"):
os.remove("{}/{}".format(TOP_DIR, name))
except FileNotFoundError:
pass
logging.info("clear done!")
else:
pass
return
def sim_on_linux(args: dict):
if args["run"] and args["wave"]:
cmd_run_with_wave = "iverilog -o {1}.out -c {0}/sim/filelist/{2}.f && \
vvp {0}/{1}.out && \
gtkwave {0}/{1}.vcd".format(TOP_DIR, args["tbfile"], tb_map[args["tbfile"]])
os.system(cmd_run_with_wave)
elif args["run"]:
cmd_run = "iverilog -o {1}.out -c {0}/sim/filelist/{2}.f && vvp {0}/{1}.out".format(TOP_DIR, args["tbfile"], tb_map[args["tbfile"]])
os.system(cmd_run)
elif args["wave"]:
if not os.path.exists("{0}/{1}.vcd".format(TOP_DIR, args["tbfile"])):
logging.error("wave file does not exists!")
exit(1)
cmd_wave = "gtkwave {0}/{1}.vcd".format(TOP_DIR, args["tbfile"])
os.system(cmd_wave)
elif args['clean']:
try:
for root, dirs, files in os.walk(TOP_DIR):
for name in files:
if name.endswith(".out"):
os.remove("{}/{}".format(TOP_DIR, name))
elif name.endswith(".vcd"):
os.remove("{}/{}".format(TOP_DIR, name))
except FileNotFoundError:
pass
logging.info("clear done!")
else:
pass
return
def args_check(args: dict):
if args['simtool'] not in ["iverilog"]:
logging.warning("Unsupported simulator, aborting process!")
exit(1)
elif args['clean'] and (args['run'] or args['wave']):
logging.warning("Parameter conflicts, 'clean' must be independent!")
exit(1)
else:
pass
return
def parse() -> dict:
parser = argparse.ArgumentParser()
# group = parser.add_mutually_exclusive_group()
# group.add_argument("-c", "--compile", action="store_true", help="Compile RTL only")
parser.add_argument("-r", "--run", action="store_true", help="使用testbench进行编译和仿真")
parser.add_argument("-w", "--wave", action="store_true", help="使用gtkwave打开vcd波形文件")
parser.add_argument("-c", "--clean", action="store_true", help="删除.out/.vcd文件")
parser.add_argument("-s", "--simtool", default="iverilog", help="指定仿真环境,默认为iverilog")
parser.add_argument("-t", "--tbfile", default="tb_core", help="指定tb文件,默认为tb_core")
args = vars(parser.parse_args())
return args
def main():
args = parse()
args_check(args)
if CUR_SYS == "win32":
logging.info("Simulation starts on win...")
sim_on_win(args)
elif CUR_SYS == "linux":
logging.info("Simulation starts on linux...")
sim_on_linux(args)
else:
logging.warning("Unknown system, aborting process!")
exit(1)
return
if __name__ == "__main__":
main()