本文档对于寄存器的属性以synopsys相关文档以及ral_model生成模型时的识别方式为基准。通常讲可以归纳为以下三个方面:读写属性read-write access、写后值modified write value、读后操作read action。注意,这里指的都是软件的操作,对于硬件侧而言,寄存器无法感知到读行为,对于写行为一般允许通过hw_wen和hw_wdata进行操作。
通过这三方面的属性,可以精准的描述一个寄存器的行为,比如:
在寄存器的xml描述中,采用如下的方式表示一个field的这三方面属性:
read-write
oneToSet
clear
需要注意的是,access是必须要进行描述的,而modifiedWriteValue和readAction则不强制。modifiedWriteValue的缺省属性为总线数据写入,readAction的缺省属性为不改变寄存器值。
因此接下来先看一下这三个方面的属性都有哪些描述符。
读写属性标记个某个寄存器(在此不区分寄存器reg和域field)是否可以被软件访问。
Access | Read | Write |
---|---|---|
read-write | 返回寄存器值 | 参见Modified Write Value |
read-only | 返回寄存器值 | 总线返回error |
write-only | 总线返回error,总线读取值不可预期 | 参见 Modified Write Value |
read-writeOnce | 返回寄存器值 | 复位后只可写一次,写操作参见 Modified Write Value,后续写操作无效 |
writeOnce | 总线返回error,总线读取值不可预期 | 复位后只可写一次,写操作参见 Modified Write Value,后续写操作无效 |
写后操作标记了某个寄存器在软件写操后数值的变化。
modifiedWriteValue | 对应bit写0 | 对应bit写1 |
---|---|---|
NA(缺省) | 清0 | 置1 |
oneToClear | 无影响 | 清0 |
oneToSet | 无影响 | 置1 |
oneToToggle | 无影响 | 翻转 |
zeroToClear | 清0 | 无影响 |
zeroToSet | 置1 | 无影响 |
zeroToToggle | 翻转 | 无影响 |
clear | 清0 | 清0 |
set | 置1 | 置1 |
读后行为标记了某个寄存器在软件读操后数值的变化。
readAction | 读 |
---|---|
NA(缺省) | 无影响 |
clear | 清0 |
set | 置1 |
结合以上三方面属性以及相关文档和ral_model的实际情况,可以得到下面的25种寄存器行为汇总和ral_model支持情况表格。
属性 | 行为 | access | modifiedWriteValue | readAction | ral是否支持 |
---|---|---|---|---|---|
rw | 软件可读可写 | read-write | NA | NA | √ |
ro | 软件可读,写返回总线报错 | read-only | NA | NA | √ |
wo | 软件可写,读数据报错 | write-only | NA | NA | √ |
w1 | 仅复位后可写一次,读数据报错 | read-writeOnce | NA | NA | |
w1c | 写1清0,软件可读 | read-write | oneToClear | NA | √ |
rc | 软件读清零,写报错 | read-only | NA | clear | √ |
rs | 软件读置1,写报错 | read-only | NA | set | √ |
wrc | 软件可读写,读清零 | read-write | NA | clear | √ |
wrs | 软件可读写,读置1 | read-write | NA | set | √ |
wc | 软件可读写,写清零 | read-write | clear | NA | √ |
ws | 软件可读写,写置1 | read-write | set | NA | √ |
wsrc | 写置1,读清0 | read-write | set | clear | |
wcrs | 写清0,读置1 | read-write | clear | set | |
w1s | 写1置1,写0不变,软件可读 | read-write | oneToSet | NA | √ |
w1t | 写1翻转,写0不变,软件可读 | read-write | oneToToggle | NA | √ |
w0c | 写0清0,写1不变,软件可读 | read-write | zeroToClear | NA | |
w0s | 写0置1,写1不变,软件可读 | read-write | zeroToSet | NA | |
w0t | 写0翻转,软件可读 | read-write | zeroToToggle | NA | |
w1src | 写1置1,写0不变,读清0 | read-write | oneToSet | clear | |
w1crs | 写1清0,写0不变,读置1 | read-write | oneToClear | set | |
w0src | 写0置1,写1不变,读清0 | read-write | zeroToSet | clear | |
w0crs | 写0清0,写1不变,读置1 | read-write | zeroToClear | set | |
woc | 写清0,读错误 | write-only | clear | NA | |
wos | 写置1,读错误 | write-only | set | NA | |
wo1 | 写置1,仅复位后第一次写有效,读错误 | writeOnce | NA | NA |
基于以上所有内容,对关键寄存器进行RTL实现。先确定一下寄存器的接口,个人是倾向于能够把寄存器的接口尽量的统一的,如有特殊情况再进行修改,因此规划的接口如下:
module reg_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
input hw_wen,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
endmodule
st代表软件操作,hw代表硬件操作。当然了软件操作更常见的信号是sel/en加wen的表示,不过我自己更喜欢wen和ren的方式所以就选了这样的接口规划。
而后需要说明的是,对于软件不可写或软件不可读的寄存器,个人倾向于在寄存器的上层进行控制。若一个寄存器不可写,那么st_wen不应该被上层调度模块拉起,且上层调度模块需要返回一个error状态给到总线。若一个寄存器不可读,那么st_ren同样不应该被拉起,且上层调度模块不应驱动总线上的读数据(使其保持旧值或高阻)同时返回error状态。基于这种思路,那么rw和wo就可以共用代码了,因为软件读的行为由上层处理。
//both for rw/wo
module reg_rw_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
input hw_wen,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
reg [WD -1:0]data_ff;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_ff <= RST;
else if(st_wen)
data_ff <= st_wdata;
else if(hw_wen)
data_ff <= hw_wdata;
end
assign rdata = data_ff;
endmodule
只读型寄存器是唯一没有寄存器实体的,直接将内部寄存器的值拉出来即可。
module reg_ro_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
assign rdata = hw_wdata;
endmodule
module reg_w1_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
output[WD -1:0]rdata
);
reg [WD -1:0]data_ff;
reg lock_en;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_ff <= RST;
else if(!lock_en && st_wen)
data_ff <= st_wdata;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
lock_en <= 1'b0;
else if(st_wen)
lock_en <= 1'b1;
end
assign rdata = data_ff;
endmodule
module reg_w1c_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
input hw_wen,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
reg [WD -1:0]data_ff;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_ff <= RST;
else if(hw_wen)
data_ff <= hw_wdata;
else if(st_wen)
data_ff <= (~st_wdata & data_ff);
end
assign rdata = data_ff;
endmodule
注意,读清寄存器软件读后必须清零,而不是清为复位值。
module reg_rc_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
input hw_wen,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
reg [WD -1:0]data_ff;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_ff <= RST;
else if(hw_wen)
data_ff <= hw_wdata;
else if(st_ren)
data_ff <= {WD{1'b0}};
end
assign rdata = data_ff;
endmodule
module reg_rs_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
input hw_wen,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
reg [WD -1:0]data_ff;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_ff <= RST;
else if(hw_wen)
data_ff <= hw_wdata;
else if(st_ren)
data_ff <= {WD{1'b1}};
end
assign rdata = data_ff;
endmodule
module reg_wrc_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
input hw_wen,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
reg [WD -1:0]data_ff;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_ff <= RST;
else if(hw_wen)
data_ff <= hw_wdata;
else if(st_wen)
data_ff <= st_wdata;
else if(st_ren)
data_ff <= {WD{1'b0}};
end
assign rdata = data_ff;
endmodule
module reg_wrs_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
input hw_wen,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
reg [WD -1:0]data_ff;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_ff <= RST;
else if(hw_wen)
data_ff <= hw_wdata;
else if(st_wen)
data_ff <= st_wdata;
else if(st_ren)
data_ff <= {WD{1'b1}};
end
assign rdata = data_ff;
endmodule
module reg_wc_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
input hw_wen,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
reg [WD -1:0]data_ff;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_ff <= RST;
else if(hw_wen)
data_ff <= hw_wdata;
else if(st_wen)
data_ff <= {WD{1'b0}};
end
assign rdata = data_ff;
endmodule
module reg_wc_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
input hw_wen,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
reg [WD -1:0]data_ff;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_ff <= RST;
else if(hw_wen)
data_ff <= hw_wdata;
else if(st_wen)
data_ff <= {WD{1'b1}};
end
assign rdata = data_ff;
endmodule
module reg_wsrc_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
input hw_wen,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
reg [WD -1:0]data_ff;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_ff <= RST;
else if(hw_wen)
data_ff <= hw_wdata;
else if(st_wen)
data_ff <= {WD{1'b1}};
else if(st_ren)
data_ff <= {WD{1'b0}};
end
assign rdata = data_ff;
endmodule
module reg_wcrs_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
input hw_wen,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
reg [WD -1:0]data_ff;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_ff <= RST;
else if(hw_wen)
data_ff <= hw_wdata;
else if(st_wen)
data_ff <= {WD{1'b0}};
else if(st_ren)
data_ff <= {WD{1'b1}};
end
assign rdata = data_ff;
endmodule
module reg_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
input hw_wen,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
reg [WD -1:0]data_ff;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_ff <= RST;
else if(hw_wen)
data_ff <= hw_wdata;
else if(st_wen)
data_ff <= data_ff | st_wdata;
end
assign rdata = data_ff;
endmodule
软件写1翻转,实际对应的就是原有值与写入值的亦或。
module reg_w1t_field #(
parameter WD = 32,
parameter RST = {WD{1'b0}})
(
input clk,
input rst_n,
input st_wen,
input st_ren,
input [WD -1:0]st_wdata,
input hw_wen,
input [WD -1:0]hw_wdata,
output[WD -1:0]rdata
);
reg [WD -1:0]data_ff;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_ff <= RST;
else if(hw_wen)
data_ff <= hw_wdata;
else if(st_wen)
data_ff <= st_wdata ^ data_ff;
end
assign rdata = data_ff;
endmodule
其他ral_model不支持的类型我就没有都写完了,仅供参考吧。