关于毕业设计(2)图像旋转verilog设计(第一代)

目的

  1. jpg转coe文件存入bram1中
  2. 读出bram1数据,并做45度旋转
  3. 仿真产生图像数据给matlab,转化为png看效果

jpg转coe,BRAM使用

关于毕业设计(1):matlab处理jpg转coe并载入BRAM

图像旋转模块说明

旋转模块:

parameter DATA_WIDTH = 24,
parameter ADDR_WIDTH = 19 
端口输入输出功能
clkI
rstI
startI开始读取数据并旋转
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_enO图像旋转有效信号
finishO图像旋转完成信号

设计代码

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仿真数据处理生成图像

关于毕业设计(3):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可识别的图像数据。这些过程都有出错的可能。

犯错总结

  1. 实例化时漏了start
  2. 得到rotate_addr时用错了para参数(W用成了W_WIDTH)
  3. 配置bram(导致时序不对)
  4. 仿真存入mem时(打拍没有对齐导致图像平移)
  5. mem写入txt时(9bit导致蓝色噪点)
  • 5
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值