文章目录
目的
- jpg转coe文件存入bram1中
- 读出bram1数据,并做45度旋转
- 仿真产生图像数据给matlab,转化为png看效果
jpg转coe,BRAM使用
关于毕业设计(1):matlab处理jpg转coe并载入BRAM
图像旋转模块说明
旋转模块:
parameter DATA_WIDTH = 24,
parameter ADDR_WIDTH = 19
端口 | 输入输出 | 功能 |
---|---|---|
clk | I | |
rst | I | |
start | I | 开始读取数据并旋转 |
di[DATA_WIDTH-1:0] | I | 读入数据 |
do[DATA_WIDTH-1:0] | O | 输出数据 |
addr_get[ADDR_WIDTH-1:0] | O | 读取图像数据的原地址 |
addr_rotate[ADDR_WIDTH-1:0] | O | 需要存储的图像旋转后的地址 |
addr_rot_en | O | 图像旋转有效信号 |
finish | O | 图像旋转完成信号 |
设计代码
module rotate
#(
parameter DATA_WIDTH = 24,
parameter ADDR_WIDTH = 19 //600*480 用了19bit
)
(
input clk,
input rst_n,
input start,
input[DATA_WIDTH-1:0] di,
output reg[DATA_WIDTH-1:0] do,
output[ADDR_WIDTH-1:0] addr_get,
output[ADDR_WIDTH-1:0] addr_rotate,
output addr_rot_en,
output finish
);
localparam PICTURE_H_WIDTH = 9 ;//按512以内计算
localparam PICTURE_W_WIDTH = 9 ;//按512以内计算
localparam PICTURE_H = 375 ;//
localparam PICTURE_W = 500 ;// 500*375
reg[PICTURE_W_WIDTH-1:0] addr_get_x;//列-宽
reg[PICTURE_H_WIDTH-1:0] addr_get_y;//行-高
wire[PICTURE_W_WIDTH-1:0] addr_rotate_x;
wire[PICTURE_H_WIDTH-1:0] addr_rotate_y;
reg rst,rst_r1;//异步复位同步释放
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
rst_r1 <= 1'b0;
rst <= 1'b0;
end
else begin
rst_r1 <= 1'b1;
rst <= rst_r1;
end
//取原图
always@(posedge clk or negedge rst)begin
if(!rst)begin
addr_get_x <= 'b0;
addr_get_y <= 'b0;
end
else if(start)begin
if(addr_get_x == PICTURE_W - 1'b1)
addr_get_x <= 'b0;
else
addr_get_x <= addr_get_x + 1'b1;
if( (addr_get_y == PICTURE_H - 1'b1) && (addr_get_x == PICTURE_W- 1'b1))
addr_get_y <= 'b0;
else if(addr_get_x == PICTURE_W - 1'b1 )
addr_get_y <= addr_get_y + 1'b1;
end
end
assign addr_get = addr_get_y * PICTURE_W + addr_get_x;
//这里还需要等流水线完成一个像素计算的时间(需要打拍)-----打2拍
wire finish_temp = (addr_get_y == PICTURE_H- 1'b1) && (addr_get_x == PICTURE_W- 1'b1) ? 1'b1 : 1'b0;
reg finish_r1,finish_r2;
always@(posedge clk )begin
finish_r1 <= finish_temp;
finish_r2 <= finish_r1;
end
assign finish = finish_r2;
//旋转计算
localparam sinxshift8 = 181;//x=45 0.707106 << 8
localparam cosxshift8 = 181;
reg[PICTURE_W_WIDTH:0] addr_rotate_x_r;
reg[PICTURE_H_WIDTH:0] addr_rotate_y_r;
always@(posedge clk or negedge rst)begin
if(!rst)begin
addr_rotate_x_r <= 'b0;
addr_rotate_y_r <= 'b0;
end
else begin
//addr_rotate_x_r <= addr_get_x;//
//addr_rotate_y_r <= addr_get_y;//
// addr_rotate_x_r <= ( ( (addr_get_x - (PICTURE_W>>>2) ) * cosxshift8 - (addr_get_y - (PICTURE_H>>>2) ) * sinxshift8) >>> 8 ) + (PICTURE_W>>>2);
// addr_rotate_y_r <= ( ( (addr_get_x - (PICTURE_W>>>2) ) * sinxshift8 + (addr_get_y - (PICTURE_H>>>2) ) * cosxshift8) >>> 8 ) + (PICTURE_H>>>2);
addr_rotate_x_r <= ( ( (addr_get_x - 250 ) * cosxshift8 - (addr_get_y - 187 ) * sinxshift8) >>> 8 )+ 250;
addr_rotate_y_r <= ( ( (addr_get_x - 250 ) * sinxshift8 + (addr_get_y - 187 ) * cosxshift8) >>> 8 )+ 187;
// if( addr_rotate_x_r>454 && addr_rotate_y_r>96 && addr_rotate_y_r <190 )
// $display("%d ,x:%d,y:%d;-----x:%d y:%d,all: %d;valid:%d", $time,addr_get_x,addr_get_y,addr_rotate_x_r,addr_rotate_y_r,addr_rotate,addr_rot_en); //查错时用的
end
end
assign addr_rotate_x = addr_rotate_x_r;//这里先暂留一个中间变量,为第二版代码做准备
assign addr_rotate_y = addr_rotate_y_r;
assign addr_rotate = addr_rotate_y * PICTURE_W + addr_rotate_x;
//旋转有效性:无效则bram的we=0
//wire addr_rot_en1 = addr_rotate_x_r[PICTURE_W_WIDTH] | addr_rotate_y_r[PICTURE_H_WIDTH] ? 1'b0 : 1'b1;//最高位为1即负数,或超过最大值
wire addr_rot_en1 = addr_rotate_x_r>=0 && addr_rotate_x_r < PICTURE_W ? 1'b1: 1'b0;
wire addr_rot_en2 = addr_rotate_y_r>=0 && addr_rotate_y_r < PICTURE_H ? 1'b1: 1'b0;
reg start_r1,start_r2;
always@(posedge clk )begin
start_r1 <= start;
start_r2 <= start_r1;
end
wire addr_rot_en3 = start_r2;
assign addr_rot_en = addr_rot_en1 & addr_rot_en3 & addr_rot_en2;
//数据需要打一拍和地址输出同步
initial do = 'd0;
always@(posedge clk)begin
do <= di;
end
endmodule
仿真代码
module rotate_tb(
);
parameter DATA_WIDTH = 24;
parameter ADDR_WIDTH = 19; // 用了19bit
parameter PIX = 187500;//一共有多少个像素点
parameter PICTURE_H = 375 ;//
parameter PICTURE_W = 500 ;// 500*375
reg clk,rst_n,start;
wire[DATA_WIDTH-1:0] di;
wire[DATA_WIDTH-1:0] do;
wire[ADDR_WIDTH-1:0] addr_get;
wire[ADDR_WIDTH-1:0] addr_rotate;
wire addr_rot_en;
wire finish;
reg[ADDR_WIDTH-1:0] addr_rotate_r;
always @(posedge clk)
addr_rotate_r <= addr_rotate;
reg[DATA_WIDTH-1:0]mem [0:PIX-1];
integer i,file;
initial begin
for(i=0;i< PIX ; i = i+1) mem[i]=0;
// wait(addr_rot_en);//图像转换有效
repeat(PIX)begin
@(posedge clk);
wait(addr_rot_en);//图像转换有效
mem[addr_rotate_r] = do;
end
start=0;
// file = $fopen("S:/19_example/fpga_example/graduate_project/fpga_gen_picture.txt","w"); // 初始化文件
// i=0;
// repeat(PIX)begin
// // $display("%d , %h", i , mem[i]);//打印成一列
// if(i% (PICTURE_W) == PICTURE_W-1)//仿真可用
// $fwrite(file,"%h\n",mem[i]);//打印成二维矩阵
// else
// $fwrite(file,"%h ",mem[i]);//
// i= i+1;
// end
// $fclose(file);
file = $fopen("S:/19_example/fpga_example/graduate_project/fpga_gen_picture_r.txt","w"); // 初始化文件
i=0;
repeat(PIX)begin
// $display("%d , %h", i , mem[i]);//打印成一列
if(i%PICTURE_W == PICTURE_W-1)//仿真可用
$fwrite(file,"%d\n",mem[i][23:16]);//打印成二维矩阵
else
$fwrite(file,"%d ",mem[i][23:16]);//
i= i+1;
end
$fclose(file);
file = $fopen("S:/19_example/fpga_example/graduate_project/fpga_gen_picture_g.txt","w"); // 初始化文件
i=0;
repeat(PIX)begin
if(i%PICTURE_W == PICTURE_W-1)//仿真可用
$fwrite(file,"%d\n",mem[i][15:8]);//打印成二维矩阵
else
$fwrite(file,"%d ",mem[i][15:8]);//
i= i+1;
end
$fclose(file);
file = $fopen("S:/19_example/fpga_example/graduate_project/fpga_gen_picture_b.txt","w"); // 初始化文件
i=0;
repeat(PIX)begin
if(i%PICTURE_W == PICTURE_W-1)//仿真可用
$fwrite(file,"%d\n",mem[i][7:0]);//打印成二维矩阵
else
$fwrite(file,"%d ",mem[i][7:0]);//
i= i+1;
end
$fclose(file);
$stop;
end
always #1 clk =~clk;
initial begin
rst_n=0;
clk=0;
start=0;
#10 rst_n=1;
#10 start=1;
end
rotate UUT
(
.clk(clk),.rst_n(rst_n),.start(start),.di(di),.do(do),
.addr_get(addr_get),.addr_rotate(addr_rotate),.addr_rot_en(addr_rot_en),.finish(finish)
);
blk_mem_gen_0 UUT_bram0
(
.addra(addr_get),
.clka(clk),
.douta(di),
.ena(start)
);
endmodule
matlab对verilog仿真数据处理生成图像
以下问题均已解决,代码中不存在下述问题了。记叙以后反思自己的问题。
设计中遇到的问题
设计后波形不对
设计仿真过程中我遇到了两个问题,一个问题是实例化时漏掉了start导致坐标不增加。另外一个问题是旋转后的坐标小于10000。
第二个问题原因有两种,一种是误判把坐标当成负数;另外是计算出来的值就是小于10000的。
经查,果然是计算的过程出了问题。
正确的计算如下:
assign addr_rotate = addr_rotate_y * PICTURE_W + addr_rotate_x;//错时把PICTURE_W 写成了PICTURE_W _WIDTH,复制真的不靠谱,写代码真的要仔细!
对于仿真得到的图像有噪点
先排除输出的数据(仿真函数的问题),则把旋转的坐标直接等于原坐标。实验。
一口老血喷上来。证明了仿真打印的文件过程没问题,问题在图像旋转的过程。其次,这个图像的噪点也太多了。
关于噪点:猜想一个原因是图像量化为coe文件时,另一个原因是fpga读取输出的过程。
经过实验,图像量化为coe过程(直接把读入的数据赋值给进coe里)不影响图片的质量。那么问题在FPGA这边了。
左边是MATLAB里的R矩阵,右边是经过FPGA后输出的R矩阵。
下图是matlab生成的“coe”文件(这里是故意输出为十进制来肉眼判断的,coe文件是十六进制的数据)(fpga的输入数据,第一列为R,第二列为G,第三列为B),和上图右边比较,可以看到和红框内第一行完全一致,说明问题在FPGA处理上面。
经查,fpga输出的每行的前两个数据都是上一行的最末两个数据。(相当于平移了2个单位)
查验波形:
故问题不在仿真函数打印、而仿真存入mem或图像处理的打拍上没有对齐!
从打拍上来看,addr=2时才输出第一个数据。(bram使用了primitives模式)
而不使用primitives模式时,时序如下:(手贱了,在生成bram时点错了导致的时序不对)
即start=1,clk上升沿,addr=0取到的数c06013在下个cycle出现。
旋转地址需要经过一个DFF因此也会延迟一个cycle出现。
在设计过程中,因为di到do(预期设计打一拍,以防后续的调整),所以确实是addr_get=2时,do出现第一个地址的输入数据。(和手画的时序图能对上)
下图中可以看到mem[1]写入了addr_get=0时取出的、打1拍后的数据c06013。
因此需要调整的是存储到mem里的仿真语句!(还有上述的bram配置)
tb中,将addr_rotate打一拍再存入mem中!
结果:mem[0]写入了addr_get=0时取出的、打1拍后的数据c06013。
当然给出的图像还是有噪点。(因为这个打拍只是把整个图像平移了两个像素)
左边是从jpg读到的R矩阵(还未处理成coe), 右边是fpga仿真的txt的R矩阵数据,可以看到完全一致。说不过去了啊!!!
不可能,不能有玄学!
噪点是蓝色的,看看B矩阵,果然!!!!
从"coe"的第三列可以看到数据确实很小,那么就是fpga处理的问题了?
但仿真得到的结果(左边)和coe完全一致啊!
那就是仿真函数写入txt的问题了。查txt,看到比255(8bit)还大的数据,怎么可能呢?
追代码:
我想杀了自己。居然写了9bit进去。
终于成了。我果然是个bug小能手。
关于旋转的问题(宏观)
旋转45度,讲道理这么写代码(逆时针)确实结果是这样的。(因为转轴基准在左上角啊!!!)
如果这么写:(顺时针)
结果大致如下:
而matlab旋转45度的结果:(以图片中心作为转轴)(逆时针)
注意到不同了,赶紧改rtl !!!
要注意的是,移位运算的优先级比+更低!!!所以这样得到的地址全是0。
用para的参数得到的基准好像不太行。500>>2 ,375>>2好像确实把转轴给变了(不是中间值250或者187)。
直接用数字:
还有俩小三角,应该去掉,明天继续改!
关于旋转的问题(小问题)
图中1不太清楚,
图中2处是地址坐标超出了W的最大值,故右边多出来的图像到左边去了。
图中3处是地址坐标变负了之后(被当成很大的值),故左边多出来的图像跑到右边去了。
举个例子,4bit数据(含1bit符号位),4’b0111是最大值7,而7+1之后变成4’b1000,如果只看低三位,则相当于右边多出来的图像到左边去了。4’b0000是最小的正数0,0-1后变为4‘b1111,如果只看低三位,则相当于左边多出来的图像跑到右边去了。
看了下代码,因为我设计时W位宽10,H位宽10,并且把最高为为1当成负数(旋转无效)。但是实际上w=500,h=375(都只有9bit)因此计算时超出的部分(负数/比这更大的数)都没有被考虑到。
经过实验这种方法是不行的,还得判断x,y是否在0~max之间。最高位判断为0即有效数据的方法不行。(除非图像是1024,512,256之类的长宽,否则都会出问题),比如图像w=500,但是实际上9bit=511,那么500-511这一段数就会跑到0-11这段地址里去。导致图像里多些东西。为什么要最高位判断呢,因为这样可以节约电路面积!(判断大小和判断最高位是否为1,两者面积差别很大!)
修改为0~max之间,结果仍然是这样。
使用display打印这些额外的部分,看看有什么猫腻。
核验出现三角形的额外区域:x:445-500,y:96-190,打印发现en都是为0的,并不可能写进存储器里,只有可能是仿真的mem出了问题。
果然。
问题其实在仿真tb里。没有等到en信号就把数据给写进mem,导致载入txt,matlab显示的数据是错误的(实际上fpga的处理是没有问题的)
当当当!第一代完成了!
分析问题的逻辑
找出可能出问题的点再去分析
从图像转coe(涉及到数据处理)、coe数据读取到bram、数据在rtl中计算传输、仿真函数写入到txt文件、txt文件处理为matlab可识别的图像数据。这些过程都有出错的可能。
犯错总结
- 实例化时漏了start
- 得到rotate_addr时用错了para参数(W用成了W_WIDTH)
- 配置bram(导致时序不对)
- 仿真存入mem时(打拍没有对齐导致图像平移)
- mem写入txt时(9bit导致蓝色噪点)