FPGA 边缘检测(摄像头v5640)

整体架构设计在这里插入图片描述

代码模块分析

总体分析

cmos 摄像头配置模块

iic 进行发数据或者读数据,此处为读数据/分频

capture 采集数据模块

rgb2gray 灰度转化(0~255)___彩色信息多,灰度化有助于提高运算速度

gauss_filter 高斯滤波器___线性平滑滤波,降低高斯噪音,即服从正太分布的噪声,图像去噪

gray2bin 二值化(0或1)__防止在灰度处理后信息还是过多

sobel 基于一阶导数的边缘检测算子/离散性差分算子__运算图像亮度函数的灰度近似值

sdram_ctrl 数据缓存__含乒乓操作、读写异步fifo (fifo的ram的内存太小故使用sdram

vga_interface 显示__含buf缓存

顶层代码

设计总流程

  1. 在上电等待20ms后,利用IIC协议进行摄像头的配置,
    配置完252个寄存器后,会输出一个配置完成有效信号给摄像头采集模块 capture;

  2. capture接收摄像头配置完成的信号,当场同步信号拉低后,且行参考信号有效时进行数据的采集。但是摄像头的数据是把16位RGB拆分为高八位和低八位发送的,我们需要通过移位+位拼接的方式把两个8bit数据合并成16bit数据输出,同时为了SDRAM模块更好的识别帧头和帧尾,
    在图像的第一个像素点以及最后一个像素点的时候分别拉高sop和eop信号,其余像素点拉低。

  3. 数据处理:包括图像的灰度转化、高斯滤波、二值化,和Sobel边沿检测等。

  4. sdram读写操作:主要⽤于控制数据流,使显⽰屏帧与帧之间切换瞬间完成。

  5. 显示(vga):利用VGA接口将数据显示到显示屏上。

module top(
    input               clk     ,
    input               rst_n   ,

    //摄像头接口
    input               cmos_vsync      ,
    input               cmos_pclk       ,
    input               cmos_href       ,
    input   [7:0]       cmos_din        ,
    
    output              cmos_xclk       ,
    output              cmos_pwdn       ,
    output              cmos_reset      ,
    output              cmos_scl        ,
    inout               cmos_sda        ,
    //sdram
    output              sdram_clk       ,
    output              sdram_cke       ,   
    output              sdram_csn       ,   
    output              sdram_rasn      ,   
    output              sdram_casn      ,   
    output              sdram_wen       ,   
    output  [1:0]       sdram_bank      ,   
    output  [12:0]      sdram_addr      ,   
    inout   [15:0]      sdram_dq        ,   
    output  [1:0]       sdram_dqm       ,
    output  [15:0]      vga_rgb         , 
    output              vga_hsync       ,
    output              vga_vsync

);
//信号定义  
    wire            cfg_done        ;
    wire            clk_cmos        ;
    wire    [15:0]  pixel           ; 
    wire            pixel_vld       ; 
    wire            pixel_sop       ; 
    wire            pixel_eop       ; 

    wire            mem_din_sop     ; 
    wire            mem_din_eop     ; 
    wire            mem_din_vld     ; 
    wire    [15:0]  mem_din         ; 

    wire            clk_100m        ;
    wire            clk_100m_s      ;
    wire            clk_vga         ;
    wire            pclk            ;
    wire            rd_req          ; 
    wire    [15:0]  dout            ; 
    wire            dout_vld        ; 
    
    wire            sys_rst_n       ;
    
    assign sys_rst_n = rst_n & cfg_done;
    assign cmos_xclk = clk_cmos;
    assign sdram_clk = clk_100m_s;

//模块例化
    pll0 u_pll0(
	.areset (~rst_n     ),
	.inclk0 (clk        ),
	.c0     (clk_cmos   ),
    .c1     (clk_vga    )
   );
    
    pll1 u_pll1(
	.areset (~rst_n     ),
	.inclk0 (clk        ),
	.c0     (clk_100m   ),
	.c1     (clk_100m_s ),
	.locked ()
    );


    iobuf u_iobuf(//io端口为直接连接FPGA与外部芯片
	.datain     (cmos_pclk  ),
    .dataout    (pclk       ) 
    );

    cmos_top u_cmos(
    /*input           */.clk     (clk       ),
    /*input           */.rst_n   (rst_n     ),
    /*output          */.scl     (cmos_scl  ),
    /*inout           */.sda     (cmos_sda  ),
    /*output          */.pwdn    (cmos_pwdn ),
    /*output          */.reset   (cmos_reset),
    /*output          */.cfg_done(cfg_done  )
    );

    capture u_capture(
    /*input           */.clk     (pclk      ),
    /*input           */.rst_n   (rst_n     ),
    /*input           */.vsync   (cmos_vsync),
    /*input           */.href    (cmos_href ),
    /*input   [7:0]   */.din     (cmos_din  ),
    /*input           */.enable  (cfg_done  ),
    /*output  [15:0]  */.dout    (pixel     ),
    /*output          */.dout_vld(pixel_vld ),
    /*output          */.dout_sop(pixel_sop ),
    /*output          */.dout_eop(pixel_eop )
    );

`ifdef ENABLE_IMAG_PROCESS
    wire            ip_dout_sop     ; 
    wire            ip_dout_eop     ; 
    wire            ip_dout_vld     ; 
    wire    [15:0]  ip_dout         ; 

    imag_process u_ip(
    /*input           */.clk         (pclk          ),
    /*input           */.rst_n       (rst_n         ),
    /*input           */.din_sop     (pixel_sop     ),
    /*input           */.din_eop     (pixel_eop     ),
    /*input           */.din_vld     (pixel_vld     ),
    /*input   [15:0]  */.din         (pixel         ),//RGB565
    /*output          */.dout_sop    (ip_dout_sop   ),
    /*output          */.dout_eop    (ip_dout_eop   ),
    /*output          */.dout_vld    (ip_dout_vld   ),
    /*output  [15:0]  */.dout        (ip_dout       ) 
    );
    
    assign mem_din     = ip_dout    ; 
    assign mem_din_vld = ip_dout_vld; 
    assign mem_din_sop = ip_dout_sop; 
    assign mem_din_eop = ip_dout_eop; 

`else
    assign mem_din     = pixel    ; 
    assign mem_din_vld = pixel_vld; 
    assign mem_din_sop = pixel_sop; 
    assign mem_din_eop = pixel_eop; 
`endif 

    sdram_controller u_mem_controller(
    /*input           */.clk     (clk_100m      ),//100M 控制器主时钟
    /*input           */.clk_in  (pclk          ),//数据输入时钟
    /*input           */.clk_out (clk_vga       ),//数据输出时钟 75m
    /*input           */.rst_n   (rst_n         ),
    /*input   [15:0]  */.din     (mem_din       ),
    /*input           */.din_vld (mem_din_vld   ),
    /*input           */.din_sop (mem_din_sop   ),
    /*input           */.din_eop (mem_din_eop   ),
    /*input           */.rd_req  (rd_req        ),//读请求
    /*output  [15:0]  */.dout    (dout          ),//
    /*output          */.dout_vld(dout_vld      ),
    //sdram接口
    /*output          */.cke     (sdram_cke     ),
    /*output          */.csn     (sdram_csn     ),
    /*output          */.rasn    (sdram_rasn    ),
    /*output          */.casn    (sdram_casn    ),
    /*output          */.wen     (sdram_wen     ),
    /*output  [1:0]   */.bank    (sdram_bank    ),
    /*output  [12:0]  */.addr    (sdram_addr    ),
    /*inout   [15:0]  */.dq      (sdram_dq      ),
    /*output  [1:0]   */.dqm     (sdram_dqm     )    
);

    vga_interface u_vga(
    /*input           */.clk      (clk_vga   ),
    /*input           */.rst_n    (sys_rst_n ),
    /*input   [15:0]  */.din      (dout      ),
    /*input           */.din_vld  (dout_vld  ),
    /*output          */.rdy      (rd_req    ),
    /*output  [15:0]  */.vga_rgb  (vga_rgb   ),
    /*output          */.vga_hsync(vga_hsync ),
    /*output          */.vga_vsync(vga_vsync )
    );

endmodule

摄像头配置代码

  1. 利用IIC协议来写摄像头的控制模块,采用状态机加计数器的方式来设计配置模块;
  2. 当上电之后计数20ms,之后就可以进行摄像头的配置,有一个配置完成信号config_flag,
  3. 当配置完252个寄存器后,配置信号有效。
  4. 配置模块主要就是通过IIC_master模块向摄像头里面写入数据,完成配置。
  5. 发送数据是以任务的方式发请求、命令和数据。
module cmos_top(
    input           clk     ,
    input           rst_n   ,
    
    output          scl     ,
    inout           sda     ,
    output          pwdn    ,
    output          reset   ,

    output          cfg_done

);
//信号定义

    wire            req         ;
    wire    [3:0]   cmd         ;
    wire            done        ;
    wire    [7:0]   dout        ;
    wire            i2c_scl     ; 
    wire            i2c_sda_i   ; 
    wire            i2c_sda_o   ; 
    wire            i2c_sda_oe  ;



//模块例化


 cmos_config u_cfg(
    /*input               */.clk         (clk       ),
    /*input               */.rst_n       (rst_n     ),
    //i2c_master
    /*output              */.req         (req       ),
    /*output      [3:0]   */.cmd         (cmd       ),
    /*output      [7:0]   */.dout        (dout      ),
    /*input               */.done        (done      ),
    /*output              */.config_done (cfg_done  )
);


 i2c_master u_i2c(
    /*input               */.clk         (clk       ),
    /*input               */.rst_n       (rst_n     ),
    /*input               */.req         (req       ),
    /*input       [3:0]   */.cmd         (cmd       ),
    /*input       [7:0]   */.din         (dout      ),
    /*output      [7:0]   */.dout        (          ),
    /*output              */.done        (done      ),
    /*output              */.slave_ack   (          ),
    /*output              */.i2c_scl     (scl       ),
    /*input               */.i2c_sda_i   (i2c_sda_i ),
    /*output              */.i2c_sda_o   (i2c_sda_o ),
    /*output              */.i2c_sda_oe  (i2c_sda_oe)   
    );

    assign i2c_sda_i = sda;
    assign sda = i2c_sda_oe?i2c_sda_o:1'bz;
    assign pwdn =  1'b0;
    assign reset = 1'b1;


endmodule 

图片处理代码

`include"param.v"
module imag_process(

    input           clk         ,
    input           rst_n       ,
    
    input           din_sop     ,
    input           din_eop     ,
    input           din_vld     ,
    input   [15:0]  din         ,//RGB565

    output          dout_sop    ,
    output          dout_eop    ,
    output          dout_vld    ,
    output  [15:0]  dout         
);

//信号定义
    wire            gray_sop    ; 
    wire            gray_eop    ; 
    wire            gray_vld    ; 
    wire    [7:0]   gray        ;

    wire            gray_din_sop; 
    wire            gray_din_eop; 
    wire            gray_din_vld; 
    wire    [7:0]   gray_din    ; 
    
    wire            binary_sop  ; 
    wire            binary_eop  ; 
    wire            binary_vld  ; 
    wire            binary      ; 

    wire            sobel       ; 
    wire            sobel_sop   ; 
    wire            sobel_eop   ; 
    wire            sobel_vld   ; 

//模块例化
//灰度化
 rgb2gray u_gray(
    /*input           */.clk         (clk       ),
    /*input           */.rst_n       (rst_n     ),
    /*input           */.din_sop     (din_sop   ),
    /*input           */.din_eop     (din_eop   ),
    /*input           */.din_vld     (din_vld   ),
    /*input   [15:0]  */.din         (din       ),//RGB565
    /*output          */.dout_sop    (gray_sop  ),
    /*output          */.dout_eop    (gray_eop  ),
    /*output          */.dout_vld    (gray_vld  ),
    /*output  [7:0]   */.dout        (gray      ) //灰度输出
);

`ifdef  ENABLE_GAUSS //高斯
    wire            gs_dout_sop ; 
    wire            gs_dout_eop ; 
    wire            gs_dout_vld ; 
    wire    [7:0]   gs_dout     ; 

    //高斯平滑处理
    gauss_filter u_guass(
    /*input           */.clk         (clk           ),
    /*input           */.rst_n       (rst_n         ),
    /*input           */.din_sop     (gray_sop      ),
    /*input           */.din_eop     (gray_eop      ),
    /*input           */.din_vld     (gray_vld      ),
    /*input   [7:0]   */.din         (gray          ),//灰度输入
    /*output          */.dout_sop    (gs_dout_sop   ),
    /*output          */.dout_eop    (gs_dout_eop   ),
    /*output          */.dout_vld    (gs_dout_vld   ),
    /*output  [7:0]   */.dout        (gs_dout       ) //灰度输出     
    );
    assign gray_din_sop = gs_dout_sop  ; 
    assign gray_din_eop = gs_dout_eop  ; 
    assign gray_din_vld = gs_dout_vld  ; 
    assign gray_din     = gs_dout      ; 

`else
    assign gray_din_sop = gray_sop  ; 
    assign gray_din_eop = gray_eop  ; 
    assign gray_din_vld = gray_vld  ; 
    assign gray_din     = gray      ; 
`endif
    //二值化使图像明暗不明显部分丢失,最后在做边缘检测时显现的图像失真严重
    gray2bin u_bin(
    /*input           */.clk         (clk           ),
    /*input           */.rst_n       (rst_n         ),
    /*input           */.din_sop     (gray_din_sop  ),
    /*input           */.din_eop     (gray_din_eop  ),
    /*input           */.din_vld     (gray_din_vld  ),
    /*input   [7:0]   */.din         (gray_din      ),//灰度输入
    /*output          */.dout_sop    (binary_sop    ),
    /*output          */.dout_eop    (binary_eop    ),
    /*output          */.dout_vld    (binary_vld    ),
    /*output          */.dout        (binary        ) //二值输出  
);

 sobel u_sobel(
    /*input           */.clk     (clk           ),
    /*input           */.rst_n   (rst_n         ),
    /*input           */.din     (binary        ),//binary 输入二值图像
    /*input           */.din_sop (binary_sop    ),//binary_sop
    /*input           */.din_eop (binary_eop    ),//binary_eop
    /*input           */.din_vld (binary_vld    ),//binary_vld
    /*output          */.dout    (sobel         ),
    /*output          */.dout_sop(sobel_sop     ),
    /*output          */.dout_eop(sobel_eop     ),
    /*output          */.dout_vld(sobel_vld     )
);

    assign dout_sop = sobel_sop;
    assign dout_eop = sobel_eop; 
    assign dout_vld = sobel_vld;
    assign dout     = {16{sobel}}; 

endmodule 


vga代码

`include "param.v"
/****************************************************
vga显示
设计思路
1. 计数器来设计行有效与场有效区域
2. 在行场有效显示区域进行输出数据  
3. 用一个fifo来缓存数据             
****************************************************/
module   vga_interface (    //1280*720
    input           clk      ,//75MHz
    input           rst_n    ,
    input   [15:0]  din      ,
    input           din_vld  ,
    output          rdy      ,
    output  [15:0]  vga_rgb  ,
    output          vga_hsync,
    output          vga_vsync
);

//信号定义

    reg     [10:0]      cnt_h       ;
    wire                add_cnt_h   ;
    wire                end_cnt_h   ;
    reg     [9:0]       cnt_v       ;
    wire                add_cnt_v   ;
    wire                end_cnt_v   ;

    reg                 h_vld       ;
    reg                 v_vld       ;
    
    reg                 hsync       ;
    reg                 vsync       ;
    reg                 rd_req      ;         

    wire                rdreq       ; 
    wire                wrreq       ; 
    wire                empty       ; 
    wire                full        ; 
    wire    [15:0]      q_out       ; 
    wire    [3:0]       usedw       ; 



//计数器
    always @(posedge clk or negedge rst_n) begin 
        if (rst_n==0) begin
            cnt_h <= 0; 
        end
        else if(add_cnt_h) begin
            if(end_cnt_h)
                cnt_h <= 0; 
            else
                cnt_h <= cnt_h+1 ;
       end
    end
    assign add_cnt_h = 1;
    assign end_cnt_h = add_cnt_h  && cnt_h == `H_TP-1 ;
    
    always @(posedge clk or negedge rst_n) begin 
        if (rst_n==0) begin
            cnt_v <= 0; 
        end
        else if(add_cnt_v) begin
            if(end_cnt_v)
                cnt_v <= 0; 
            else
                cnt_v <= cnt_v+1 ;
       end
    end
    assign add_cnt_v = end_cnt_h;
    assign end_cnt_v = add_cnt_v  && cnt_v == `V_TP-1 ;

//h_vld 
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            h_vld <= 1'b0;
        end
        else if(cnt_h == `H_START-1)begin
            h_vld <= 1'b1;
        end
        else if(cnt_h == `H_END-1)begin 
            h_vld <= 1'b0;
        end 
    end

//v_vld
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            v_vld <= 1'b0;
        end
        else if(end_cnt_h && cnt_v == `V_START)begin
            v_vld <= 1'b1;
        end
        else if(end_cnt_h && cnt_v == `V_END)begin
            v_vld <= 1'b0;
        end
    end

//rd_req
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            rd_req <= 1'b0;
        end
        else if(usedw <= 4)begin
            rd_req <= 1'b1;
        end
        else if(usedw >= 12)begin
            rd_req <= 1'b0;
        end
    end
//hsync
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            hsync <= 0;
        end
        else if(add_cnt_h && cnt_h == `H_SW-1)begin
            hsync <= 1'b1;
        end
        else if(add_cnt_h && cnt_h == `H_TP-1)begin 
            hsync <= 1'b0;
        end     
    end
//vsync
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            vsync <= 1'b0;
        end
        else if(add_cnt_v && cnt_v == `V_SW-1)begin
            vsync <= 1'b1;
        end
        else if(add_cnt_v && cnt_v == `V_TP-1)begin
            vsync <= 1'b0;
        end
    end

//FIFO例化
vga_buf u_buf(
	.aclr       (~rst_n     ),
	.clock      (clk        ),
	.data       (din        ),
	.rdreq      (rdreq      ),
	.wrreq      (wrreq      ),
	.empty      (empty      ),
	.full       (full       ),
	.q          (q_out      ),
	.usedw      (usedw      )
);

    assign wrreq = ~full && din_vld;
    assign rdreq = ~empty && h_vld && v_vld;

//输出
    assign rdy = rd_req;
    assign vga_rgb = (h_vld & v_vld)?q_out:0;
    assign vga_hsync = hsync;
    assign vga_vsync = vsync;


endmodule 


sdram代码

`include"param.v"
/****************************************************
SDRAM接口模块直接调用IP核,写一个控制模块
用来向SDRAM里卖弄突发写或者突发读,
由于涉及到跨时钟域的数据传输,
从摄像头采集到的数据写到SDRAM里面是慢时钟域到快时钟域,
从SDRAM里面读取数据显示到屏幕上是快时钟域到慢时钟域。
因此设计了两个异步FIFO用来缓存数据。  
****************************************************/
module sdram_ctrl (
    input               clk             ,
    input               clk_in          ,
    input               clk_out         ,
    input               rst_n           ,
    //数据输入
    input   [15:0]      din             ,//摄像头输入像素数据
    input               din_sop         ,
    input               din_eop         ,    
    input               din_vld         ,
    //数据输出
    input               rdreq           ,//vga的读数据请求
    output  [15:0]      dout            ,//输出给vga的数据
    output              dout_vld        ,//输出给vga的数据有效标志
    //sdram_interface
    output              avm_write       ,//输出给sdram 接口 IP 的写请求
    output              avm_read        ,//输出给sdram 接口 IP 的读请求
    output  [23:0]      avm_addr        ,//输出给sdram 接口 IP 的读写地址
    output  [15:0]      avm_wrdata      ,//输出给sdram 接口 IP 的写数据
    input   [15:0]      avs_rddata      ,//sdram 接口 IP 输入的读数据
    input               avs_rddata_vld  ,
    input               avs_waitrequest    
);

//参数定义
    localparam  IDLE  = 4'b0001,
                WRITE = 4'b0010,
                READ  = 4'b0100,
                DONE  = 4'b1000;

//信号定义

    reg     [3:0]       state_c     ;
    reg     [3:0]       state_n     ;

    reg     [8:0]       cnt         ;//突发读写计数器
    wire                add_cnt     ;
    wire                end_cnt     ;
    
    reg     [1:0]       wr_bank     ;//写bank
    reg     [1:0]       rd_bank     ;//读bank
    reg     [21:0]      wr_addr     ;//写地址   行地址 + 列地址
    wire                add_wr_addr ;
    wire                end_wr_addr ;
    reg     [21:0]      rd_addr     ;//读地址   行地址 + 列地址
    wire                add_rd_addr ;
    wire                end_rd_addr ;

    reg                 change_bank ;//切换bank 
    reg                 wr_finish   ;//一帧数据写完
    reg     [1:0]       wr_finish_r ;//同步到写侧
    reg                 wr_data_flag;//wrfifo写数据的标志

    reg                 wr_flag     ;
    reg                 rd_flag     ;
    reg                 flag_sel    ;
    reg                 prior_flag  ;

    wire                idle2write  ;  
    wire                idle2read   ;
    wire                write2done  ;
    wire                read2done   ;

    reg     [15:0]      rd_data     ;//rfifo读数据输出
    reg                 rd_data_vld ;

    wire    [17:0]      wfifo_data  ; 
    wire                wfifo_rdreq ;
    wire                wfifo_wrreq ;
    wire    [17:0]      wfifo_q     ;
    wire                wfifo_empty ;
    wire    [10:0]      wfifo_usedw ;
    wire                wfifo_full  ;

    wire    [15:0]      rfifo_data  ;
    wire                rfifo_rdreq ;
    wire                rfifo_wrreq ;
    wire    [15:0]      rfifo_q     ;
    wire                rfifo_empty ;
    wire                rfifo_full  ;
    wire    [10:0]      rfifo_usedw ;

//状态机
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end

    always  @(*)begin
        case(state_c)
            IDLE  :begin 
                if(idle2write)
                    state_n = WRITE;
                else if(idle2read)
                    state_n = READ;
                else 
                    state_n = state_c;
            end 
            WRITE :begin 
                if(write2done)
                    state_n = DONE;
                else 
                    state_n = state_c;
            end     
            READ  :begin 
                if(read2done)
                    state_n = DONE;
                else 
                    state_n = state_c;
            end 
            DONE  :state_n = IDLE;
            default:state_n = IDLE;
        endcase  
    end

    assign idle2write = state_c == IDLE  && (~prior_flag && wfifo_usedw >= `USER_BL);
    assign idle2read  = state_c == IDLE  && prior_flag && rfifo_usedw <= `RD_UT;
    assign write2done = state_c == WRITE && end_cnt;
    assign read2done  = state_c == READ  && end_cnt;

//计数器
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt <= 0;
        end
        else if(add_cnt)begin
            if(end_cnt)
                cnt <= 0;
            else
                cnt <= cnt + 1;
        end
    end

    assign add_cnt = (state_c == WRITE | state_c == READ) & ~avs_waitrequest; 
    assign end_cnt = add_cnt && cnt== `USER_BL-1;  

/************************读写优先级仲裁

如果读写信号同时拉高,根据上一次是读还是写进行判断下一次的读写
如果只有读信号或写信号,就进行相应的读或写操作
*****************************/
//rd_flag     ;//读请求标志
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rd_flag <= 0;
        end 
        else if(rfifo_usedw <= `RD_LT)begin   
            rd_flag <= 1'b1;
        end 
        else if(rfifo_usedw > `RD_UT)begin 
            rd_flag <= 1'b0;
        end 
    end

//wr_flag     ;//写请求标志
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            wr_flag <= 0;
        end 
        else if(wfifo_usedw >= `USER_BL)begin 
            wr_flag <= 1'b1;
        end 
        else begin 
            wr_flag <= 1'b0;
        end 
    end

//flag_sel    ;//标记上一次操作
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            flag_sel <= 0;
        end 
        else if(read2done)begin 
            flag_sel <= 1;
        end 
        else if(write2done)begin 
            flag_sel <= 0;
        end 
    end

//prior_flag  ;//优先级标志 0:写优先级高   1:读优先级高     仲裁读、写的优先级
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            prior_flag <= 0;
        end 
        else if(wr_flag && (flag_sel || (~flag_sel && ~rd_flag)))begin   //突发写优先级高
            prior_flag <= 1'b0;
        end 
        else if(rd_flag && (~flag_sel || (flag_sel && ~wr_flag)))begin   //突发读优先级高
            prior_flag <= 1'b1;
        end 
    end

/******************************************************************/    

/****************************************************
             乒乓操作(bank)
1、将第 1 帧数据存入 bank1 中, 当 bank1 中完整的存储了第 1 帧图像数据后,然后进入乒乓操作。
2、将第 2 帧数据存入 bank0 , 同时从 bank1 中读取存储的第 1 帧数据。

对于存储:只要一帧图像数据存储完成,则立刻切换到另一个乒乓块继续存储,而与是否读取完成无关。(因此如果存储的速度和读取的速度不相同,就会出现存储和读取位于同一个乒乓块,或者位于不同乒乓块)
对于读取:读取一帧数据完成后,只要存储数据和读取数据同时处于一个乒乓块,则切换到另一个乒乓块继续进行读取,否则不切换,继续在原乒乓块进行读取。

乒乓操作主要⽤于控制数据流,
在此项⽬中主要体现为先写SDRAM bank1的数据,同时读SDRAM bank3的数据,
当两块bank的数据读写完毕后,切换操作为读bank1的数据,写bank3的数据,
这样可以保持数据为完整的⼀帧,使显⽰屏帧与帧之间切换瞬间完成。

保证读取到的数据是一帧的效果,如果一帧数据没有读完,就开始写入会丢帧。                 
****************************************************/    

//wr_bank  rd_bank
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            wr_bank <= 2'b00;
            rd_bank <= 2'b11;
        end
        else if(change_bank)begin
            wr_bank <= ~wr_bank;
            rd_bank <= ~rd_bank;
        end
    end

// wr_addr   rd_addr
    always @(posedge clk or negedge rst_n) begin 
        if (rst_n==0) begin
            wr_addr <= 0; 
        end
        else if(add_wr_addr) begin
            if(end_wr_addr)
                wr_addr <= 0; 
            else
                wr_addr <= wr_addr+1 ;
       end
    end
    assign add_wr_addr = (state_c == WRITE) && ~avs_waitrequest;
    assign end_wr_addr = add_wr_addr  && wr_addr == `BURST_MAX-1 ;
    
    always @(posedge clk or negedge rst_n) begin 
        if (rst_n==0) begin
            rd_addr <= 0; 
        end
        else if(add_rd_addr) begin
            if(end_rd_addr)
                rd_addr <= 0; 
            else
                rd_addr <= rd_addr+1 ;
       end
    end
    assign add_rd_addr = (state_c == READ) && ~avs_waitrequest;
    assign end_rd_addr = add_rd_addr  && rd_addr == `BURST_MAX-1;

//wr_finish     一帧数据全部写到SDRAM
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            wr_finish <= 1'b0;
        end
        else if(~wr_finish & wfifo_q[17])begin  //写完  从wrfifo读出eop
            wr_finish <= 1'b1;
        end
        else if(wr_finish && end_rd_addr)begin  //读完
            wr_finish <= 1'b0;
        end
    end

//change_bank ;//切换bank 
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            change_bank <= 1'b0;
        end
        else begin
            change_bank <= wr_finish && end_rd_addr;
        end
    end

/****************************************************************/

/****************************************************
                 wrfifo写数据
                控制像素数据帧 写入 或 丢帧  eop和sop都表示一个像素点
1、同步:将写入sdram的一帧数据信号 wr_finish 从wirfifo的读侧同步到写侧
2、wrfifo写入数据信号定义:
①写数据:未写入数据 + (开始信号sop + 写入的一帧数据未同步完成,即正在写入数据)
②不写入数据:已经写入数据 + 结束信号eop
****************************************************/
//控制像素数据帧 写入 或 丢帧

    always  @(posedge clk_in or negedge rst_n)begin
        if(~rst_n)begin
            wr_data_flag <= 1'b0;
        end 
        else if(~wr_data_flag & ~wr_finish_r[1] & din_sop)begin//可以向wrfifo写数据
            wr_data_flag <= 1'b1;
        end
        else if(/*wr_finish_r[1] && din_sop*/wr_data_flag & din_eop)begin//不可以向wrfifo写入数据
            wr_data_flag <= 1'b0;
        end
    end

    always  @(posedge clk_in or negedge rst_n)begin //把wr_finish从wrfifo的读侧同步到写侧
        if(~rst_n)begin
            wr_finish_r <= 0;
        end
        else begin
            wr_finish_r <= {wr_finish_r[0],wr_finish};
        end
    end

/****************************************************************/

    always  @(posedge clk_out or negedge rst_n)begin
        if(~rst_n)begin
            rd_data <= 0;
            rd_data_vld <= 1'b0;
        end
        else begin
            rd_data <= rfifo_q;
            rd_data_vld <= rfifo_rdreq;
        end
    end
//wfifo设计,慢时钟域写到快时钟域读
//数据进行拼接,将eop,sop和 数据拼接到一起,用于判断能不能写入
wrfifo	wrfifo_inst (
	.aclr   (~rst_n     ),
	.data   (wfifo_data ),
	.rdclk  (clk        ),
	.rdreq  (wfifo_rdreq),
	.wrclk  (clk_in     ),
	.wrreq  (wfifo_wrreq),
	.q      (wfifo_q    ),
	.rdempty(wfifo_empty),
	.rdusedw(wfifo_usedw),
	.wrfull (wfifo_full )
	);

    assign wfifo_data = {din_eop,din_sop,din};
    assign wfifo_wrreq = ~wfifo_full & din_vld & ((~wr_finish_r[1] & din_sop) ||wr_data_flag);
    assign wfifo_rdreq = state_c == WRITE && ~avs_waitrequest;
/*wrfifo的写请求拉高条件
    wrfifo非空
    sdr从机输入数据有效信号
    (开始标志sop + 正在写入一帧数据且未完成写入  或者 wrfifo的写入数据标志
*/

//读写不能同时进行,只有dq一组数据线
//rdfifo设计 ,快时钟写到慢时钟读
rdfifo u_rdfifo(
	.aclr       (~rst_n     ),
	.data       (rfifo_data ),
	.rdclk      (clk_out    ),
	.rdreq      (rfifo_rdreq),
	.wrclk      (clk        ),
	.wrreq      (rfifo_wrreq),
	.q          (rfifo_q    ), 
	.rdempty    (rfifo_empty),
	.wrfull     (rfifo_full ),
	.wrusedw    (rfifo_usedw)
);

    assign rfifo_data = avs_rddata;
    assign rfifo_wrreq = ~rfifo_full & avs_rddata_vld;
    assign rfifo_rdreq = ~rfifo_empty & rdreq;

//输出
    assign dout       = rd_data;
    assign dout_vld   = rd_data_vld;
    assign avm_wrdata = wfifo_q[15:0];
    assign avm_write  = ~(state_c == WRITE && ~avs_waitrequest);
    assign avm_read   = ~(state_c == READ && ~avs_waitrequest);
    assign avm_addr   = (state_c == WRITE)?{wr_bank[1],wr_addr[21:9],wr_bank[0],wr_addr[8:0]}
                       :((state_c == READ)?{rd_bank[1],rd_addr[21:9],rd_bank[0],rd_addr[8:0]}
                       :0);

endmodule 



tcl

# Copyright (C) 2018  Intel Corporation. All rights reserved.
# Your use of Intel Corporation's design tools, logic functions 
# and other software and tools, and its AMPP partner logic 
# functions, and any output files from any of the foregoing 
# (including device programming or simulation files), and any 
# associated documentation or information are expressly subject 
# to the terms and conditions of the Intel Program License 
# Subscription Agreement, the Intel Quartus Prime License Agreement,
# the Intel FPGA IP License Agreement, or other applicable license
# agreement, including, without limitation, that your use is for
# the sole purpose of programming logic devices manufactured by
# Intel and sold by Intel or its authorized distributors.  Please
# refer to the applicable agreement for further details.

# Quartus Prime Version 18.1.0 Build 625 09/12/2018 SJ Standard Edition
# File: C:\Users\EDY\Desktop\fpga_coms_sdram_vga-main\cmos_sdram_vga\prj\top.tcl
# Generated on: Thu May 25 20:16:22 2023

package require ::quartus::project

set_location_assignment PIN_D15 -to vga_vsync
set_location_assignment PIN_C15 -to vga_rgb[0]
set_location_assignment PIN_B16 -to vga_rgb[1]
set_location_assignment PIN_D2 -to vga_rgb[2]
set_location_assignment PIN_B14 -to vga_rgb[3]
set_location_assignment PIN_A14 -to vga_rgb[4]
set_location_assignment PIN_B13 -to vga_rgb[5]
set_location_assignment PIN_A13 -to vga_rgb[6]
set_location_assignment PIN_B12 -to vga_rgb[7]
set_location_assignment PIN_A12 -to vga_rgb[8]
set_location_assignment PIN_B11 -to vga_rgb[9]
set_location_assignment PIN_A11 -to vga_rgb[10]
set_location_assignment PIN_B10 -to vga_rgb[11]
set_location_assignment PIN_A10 -to vga_rgb[12]
set_location_assignment PIN_B9 -to vga_rgb[13]
set_location_assignment PIN_A9 -to vga_rgb[14]
set_location_assignment PIN_C8 -to vga_rgb[15]
set_location_assignment PIN_C16 -to vga_hsync
set_location_assignment PIN_N1 -to sdram_wen
set_location_assignment PIN_R6 -to sdram_rasn
set_location_assignment PIN_R4 -to sdram_clk
set_location_assignment PIN_T6 -to sdram_csn
set_location_assignment PIN_R14 -to sdram_cke
set_location_assignment PIN_T5 -to sdram_casn
set_location_assignment PIN_E15 -to rst_n
set_location_assignment PIN_E1 -to clk
set_location_assignment PIN_R7 -to sdram_bank[0]
set_location_assignment PIN_T7 -to sdram_bank[1]
set_location_assignment PIN_N2 -to sdram_dqm[0]
set_location_assignment PIN_T14 -to sdram_dqm[1]
set_location_assignment PIN_R5 -to sdram_dq[0]
set_location_assignment PIN_T4 -to sdram_dq[1]
set_location_assignment PIN_T3 -to sdram_dq[2]
set_location_assignment PIN_R3 -to sdram_dq[3]
set_location_assignment PIN_T2 -to sdram_dq[4]
set_location_assignment PIN_R1 -to sdram_dq[5]
set_location_assignment PIN_P2 -to sdram_dq[6]
set_location_assignment PIN_P1 -to sdram_dq[7]
set_location_assignment PIN_R13 -to sdram_dq[8]
set_location_assignment PIN_T13 -to sdram_dq[9]
set_location_assignment PIN_R12 -to sdram_dq[10]
set_location_assignment PIN_T12 -to sdram_dq[11]
set_location_assignment PIN_T10 -to sdram_dq[12]
set_location_assignment PIN_R10 -to sdram_dq[13]
set_location_assignment PIN_T11 -to sdram_dq[14]
set_location_assignment PIN_R11 -to sdram_dq[15]
set_location_assignment PIN_T8 -to sdram_addr[0]
set_location_assignment PIN_P9 -to sdram_addr[1]
set_location_assignment PIN_T9 -to sdram_addr[2]
set_location_assignment PIN_R9 -to sdram_addr[3]
set_location_assignment PIN_R16 -to sdram_addr[11]
set_location_assignment PIN_T15 -to sdram_addr[12]
set_location_assignment PIN_R8 -to sdram_addr[10]
set_location_assignment PIN_N16 -to sdram_addr[6]
set_location_assignment PIN_N15 -to sdram_addr[7]
set_location_assignment PIN_P16 -to sdram_addr[8]
set_location_assignment PIN_P15 -to sdram_addr[9]
set_location_assignment PIN_L16 -to sdram_addr[4]
set_location_assignment PIN_L15 -to sdram_addr[5]
set_location_assignment PIN_D3 -to cmos_xclk
set_location_assignment PIN_F6 -to cmos_vsync
set_location_assignment PIN_C6 -to cmos_scl
set_location_assignment PIN_D6 -to cmos_sda
set_location_assignment PIN_D5 -to cmos_pclk
set_location_assignment PIN_C3 -to cmos_din[7]
set_location_assignment PIN_F5 -to cmos_din[0]
set_location_assignment PIN_G5 -to cmos_din[1]
set_location_assignment PIN_D4 -to cmos_din[2]
set_location_assignment PIN_F3 -to cmos_din[4]
set_location_assignment PIN_F2 -to cmos_din[5]
set_location_assignment PIN_E5 -to cmos_din[6]
set_location_assignment PIN_D1 -to cmos_href
set_location_assignment PIN_G2 -to cmos_pwdn
set_location_assignment PIN_F1 -to cmos_reset
set_location_assignment PIN_M1 -to cmos_din[3]

RTL

在这里插入图片描述

  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值