写了将近一个月的代码,写写删删。一开始花了几天时间撸清了思路,画好了图,下手的时候发现很多地方还是考虑不周。今天好不容易写出点样子来,暂时做个总结。
正文:
缩放倍数:(分三步)
step1:预设好的,比如3、4倍;
step2:还是预设好的,缩放因子是一个parameter。可以通过修改代码改变;
step3:缩放因子是个变量,不用改代码,可以改变的。
从一开始的思路说起吧,之前的博客有撸过算法原理,这里不赘述。上图:
理想很美好,现实很焦灼啊。。。理论上来说,模块是这样安排的:
先说插值系数生成模块,两个计数器,一个行计数cnt_i,一个列计数cnt_j,考虑到计算结果有小数,且要用到小数,遂用定点数的计算方式。Q14,例:row_map = 4*i*(2^14)/3 = i* 21844
。算出的结果取
row_int <= row_map[25 : 14];
row_dec <= row_map[13 : 0 ];
row_int为x,row_dec为u,代入后面的模块。计算还都不算麻烦,麻烦的是数据提取,就是上图的数据缓存单元。因为考虑到数据提取时,是以(x,y)做地址提取,而RAM的写端口写入的行数据为一行1280个数据,读端口需要读出的行数据只需要960个数据,为避免产生不必要的冲突,比如读比写快(可使用异步时钟,但尽量使用同步时钟为好),读地址不好控这种问题,所以在计数时,暂时以3/4这个倍数为准,每隔三个数计一个延迟。(这一点其实一开始并没有考虑到,后来写数据提取模块的时候代码写了删删了写,考虑很多方法都还是有哪里不对,所以后来在师父的指点下,干脆从源头下手,后面看起来就好很多了)。
接着是数据提取模块,个人认为最麻烦的模块,天知道在这里死了我多少脑细胞,甚至一度以为我是不是智商不够,撸不清逻辑。大脑还一片混乱,主要,是不知道怎么控读地址。取simple dual ram,缓存四行数据,因为四个坐标分别来自上下两行,想要在同一时刻读出四个坐标,就得有一个ram输出第一行数据,一个ram输出第二行数据。输出第一行数据的ram,写端口从复位开始就可以写,且写地址以1递加,读端口从第三行数据写完开始读,控读地址,实在懒得描述,后续可补,这里就直接放代码:
//RAM0
reg wea0 = 0 ;
reg enb0 = 0 ;
reg [12 : 0] addra0 = 13'b0000000000001 ;
reg [12 : 0] addrb0 ;
reg [12 : 0] cnt_rnum = 0 ;
reg [8 : 0] cnt_ram0 = 4'b0100 ;
reg [3 : 0] z0 ;
always@(posedge clk) begin //wea0
if(!resetn) begin
wea0 <= 1 ;
end
end
always@(posedge clk) begin //addra0
if(!resetn) begin
if(s_axis_df_tdata && m_axis_df_tready)
addra0 <= addra0 + 1 ;
if(addra0 == 5120) //1280*4
addra0 <= 13'b0000000000001 ;
end
end
always@(posedge clk) begin //enb0
if(addra0 == 3840) //5120-1280
enb0 <= 1 ;
end
always@(posedge clk) begin //cnt_rnum
if(x > 4) begin
if(cnt_rnum == 5119)
cnt_rnum <= 0 ;
else
cnt_rnum <= cnt_rnum + 1 ;
end
else
cnt_rnum <= 0 ;
end
always@(posedge clk) begin //cnt_ram0
if(cnt_ram0 == 180) //720/4
cnt_ram0 <= 4'b0100 ;
if(cnt_rnum == 5119)
cnt_ram0 <= cnt_ram0 + 4'b0100 ;
end
always@(posedge clk) begin //z0
if(x < 4)
z0 <= x ;
else
z0 <= x - cnt_ram0 ;
end
always@(posedge clk) begin //addrb0
case(z0)
4'd1 : addrb0 <= y ;
4'd2 : addrb0 <= y + 11'b101_0000_0000 ;
4'd3 : addrb0 <= y + 12'b1010_0000_0000 ;
4'd4 : addrb0 <= y + 12'b1111_0000_0000 ;
default : addrb0 <= y ;
endcase
end
输出第二行数据的ram,写端口从第一行数据走过了之后才可写入,读端口和前一个ram差不多。
//RAM1
reg wea1 = 0 ;
reg enb1 = 0 ;
reg [12 : 0] addra1 = 13'b0000000000001 ;
reg [12 : 0] addrb1 ;
reg [8 : 0] cnt_ram1 = 4'b0100 ;
reg [3 : 0] z1 ;
always@(posedge clk) begin //wea1
if(addra0 == 1280)
wea1 <= 1 ;
end
always@(posedge clk) begin //addra1
if(!resetn) begin
if(wea1)
addra1 <= addra1 + 1 ;
if(addra1 == 5120)
addra1 <= 13'b0000000000001 ;
end
end
always@(posedge clk) begin //enb1
if(addra0 == 3840) //5120-1280
enb1 <= 1 ;
end
always@(posedge clk) begin //cnt_ram1
if(cnt_ram1 == 179) //720/4-1
cnt_ram1 <= 4'b0100 ;
if(cnt_rnum == 5119)
cnt_ram1 <= cnt_ram1 + 4'b0100 ;
end
always@(posedge clk) begin //z1
if(x < 4)
z1 <= x ;
else
z1 <= x - cnt_ram1 ;
end
always@(posedge clk) begin //addrb1
case(z1)
4'd1 : addrb1 <= y ;
4'd2 : addrb1 <= y + 11'b101_0000_0000 ;
4'd3 : addrb1 <= y + 12'b1010_0000_0000 ;
4'd4 : addrb1 <= y + 12'b1111_0000_0000 ;
default : addrb1 <= y ;
endcase
end
需要延迟的数据就用语句延迟。这里还要加上valid信号,以便后期做一个筛选,valid为0时是无效数据,valid为1时输出为有效数据。考虑和axis接口的s_axis_tvalid合并。
最后是双线性插值模块,就是以前两个模块的输出为输入做计算,乘法器用的比较多,大概后期还要有个类似优化的过程。。。
那顶层文件里就是调用这三个模块,再做一些需要的延迟即可:
//缩放因子:value = 3/4
//源图行列数据:row_src = 1280,col_src = 720
//目标图像行列数据:row_is = 960,col_is = 540
//---------------------------------------------------------------------
`timescale 1 ns / 1 ps
module ImageScaling #(
parameter WIDTH = 24 , CWIDTH = 11 ,dec_WIDTH = 14//数据位宽 坐标位宽 小数位宽
)
(
input clk ,
input resetn ,
output [CWIDTH - 1 : 0] x ,
output [CWIDTH - 1 : 0] y ,
output [9 : 0] row_cnt ,
output [10 : 0] col_cnt ,
output reg valid ,
output [23 : 0] data00 ,
output [23 : 0] data01 ,
output [23 : 0] data10 ,
output [23 : 0] data11 ,
output [13 : 0] u_in ,
output [13 : 0] v_in ,
output [13 : 0] u1_in ,
output [13 : 0] v1_in ,
//slave
input [WIDTH-1 : 0] s_axis_scaling_tdata ,
input wire s_axis_scaling_tlast ,
output s_axis_scaling_tready ,
input wire s_axis_scaling_tuser ,
input wire s_axis_scaling_tvalid ,
//master
output [WIDTH-1 : 0] m_axis_scaling_tdata ,
output m_axis_scaling_tlast ,
input wire m_axis_scaling_tready ,
output m_axis_scaling_tuser ,
output m_axis_scaling_tvalid
);
//延迟
reg [13 : 0] u_r0 ;
reg [13 : 0] v_r0 ;
reg [13 : 0] u1_r0 ;
reg [13 : 0] v1_r0 ;
reg [13 : 0] u_r1 ;
reg [13 : 0] v_r1 ;
reg [13 : 0] u1_r1 ;
reg [13 : 0] v1_r1 ;
reg [13 : 0] u_r2 ;
reg [13 : 0] v_r2 ;
reg [13 : 0] u1_r2 ;
reg [13 : 0] v1_r2 ;
always@(posedge clk) begin
u_r0 <= u ;
v_r0 <= v ;
u1_r0 <= u1 ;
v1_r0 <= v1 ;
end
always@(posedge clk) begin
u_r1 <= u_r0 ;
v_r1 <= v_r0 ;
u1_r1 <= u1_r0 ;
v1_r1 <= v1_r0 ;
end
always@(posedge clk) begin
u_r2 <= u_r1 ;
v_r2 <= v_r1 ;
u1_r2 <= u1_r1 ;
v1_r2 <= v1_r1 ;
end
always@(posedge clk) begin
u_in <= u_r2 ;
v_in <= v_r2 ;
u1_in <= u1_r2 ;
v1_in <= v1_r2 ;
end
reg valid_0 ;
reg valid_1 ;
reg valid_2 ;
reg valid_3 ;
reg valid_4 ;
always@(posedge clk) begin
valid_0 <= valid_in ;
valid_1 <= valid_0 ;
valid_2 <= valid_1 ;
valid_3 <= valid_2 ;
valid_4 <= valid_3 ;
valid <= valid_4 ;
end
//系数生成
wire [CWIDTH - 1 : 0] x ;
wire [CWIDTH - 1 : 0] y ;
wire [dec_WIDTH-1 : 0] u ;
wire [dec_WIDTH-1 : 0] v ;
wire [dec_WIDTH-1 : 0] u1 ;
wire [dec_WIDTH-1 : 0] v1 ;
Coefficient_generation CG(
.clk (clk ) ,
.resetn (resetn ) ,
.start_cg(start_cg) , //DF in
.row_int (x ) , //CG out to DF
.col_int (y ) ,
.u (u ) , //CG out to BI
.u1 (u1 ) ,
.v (v ) ,
.v1 (v1 )
);
//数据提取
wire [WIDTH - 1 : 0] data00 ;
wire [WIDTH - 1 : 0] data01 ;
wire [WIDTH - 1 : 0] data10 ;
wire [WIDTH - 1 : 0] data11 ;
data_fetch DF(
.clk (clk ),
.resetn (resetn ),
.s_axis_df_tdata (s_axis_scaling_tdata ), //in
.s_axis_df_tlast (s_axis_scaling_tlast ),
.s_axis_df_tuser (s_axis_scaling_tuser ),
.s_axis_df_tvalid (s_axis_scaling_tvalid),
.s_axis_df_tready (s_axis_scaling_tready), //out
.start_cg(start_cg), //DF out to CG
.start_bi(start_bi), //DF out to BI
.x(x), //CG in
.y(y),
.row_cnt(row_cnt),
.col_cnt(col_cnt),
.valid(valid_in),
.doutb00 (data00), //DF out to BI
.doutb01 (data01),
.doutb10 (data10),
.doutb11 (data11),
.m_axis_df_tlast (m_axis_scaling_tlast), //out
.m_axis_df_tready (m_axis_scaling_tready), //in
.m_axis_df_tuser (m_axis_scaling_tuser ),
.m_axis_df_tvalid (m_axis_scaling_tvalid)
);
//双线性插值计算
reg [dec_WIDTH-1 : 0] u_in ;
reg [dec_WIDTH-1 : 0] u1_in ;
reg [dec_WIDTH-1 : 0] v_in ;
reg [dec_WIDTH-1 : 0] v1_in ;
bilinear_interpolation BI(
.clk (clk ),
.resetn (resetn ),
.start_bi(start_bi), //DF in
.data00 (data00 ), //DF in
.data01 (data01 ),
.data10 (data10 ),
.data11 (data11 ),
.u_in (u_in ), //CG in
.u1_in (u1_in ),
.v_in (v_in ),
.v1_in (v1_in ),
.data_o (m_axis_scaling_tdata ) //OUT
);
endmodule
值得一提的是,动手写之前考虑好所有是个很6的技能啊,还需慢慢掌握。尽管是个小模块,但也是成长的一步嘛。后面其实事情挺多的,要学的也越来越清晰。加油啊,要成为攻城狮的我们!