文章目录
- VGA
- 基本概念:
- 水平扫描:
- 垂直扫描:
- 时序如下:
- 端口设计
- 疑问
- 为什么需要输出那么多端口
- 不输出时钟怎么保证电子枪移动速度符合时序
- VGA转HDMI
- 仿真电路图
- 代码
- 总结:VGA看野火电子教程
- HDMI
- TMDS传输原理
- 为什么使用TMDS
- HDMI接口结构
- HDMI_crl: 实现VGA转HDMI
- 构成1:TMDS encoder模块
- 构成2:并转串_差分输出模块
- 调用原语:DDR(时钟沿双边采集)
- 回顾:
为了做图像处理, 现在我们开始研究VGA与HDMI
VGA
基本概念:
水平扫描:
- 有效数据段:显示图像的部分。
- 右边界区域(Right Border):扫描完有效数据后的空白区域。
- 水平前沿(Front Porch):扫描完有效数据但未收到同步信号前的空白时间段
- 行同步信号脉冲(Sync Pulse):通知电子枪回到左侧。
- 水平后沿(Back Porch):电子枪回到左侧的时间段。
- 左边界区域(LEFT Border):显示图像前的空白区域。
垂直扫描:
- 有效行段:显示图像的行。
- 下边界区域(Bottom Border):扫描完所有行后的空白区域。
- 垂直前沿(Front Porch):扫描完所有行但未收到同步信号前的空白时间段
- 场同步信号脉冲(Sync Pulse):通知电子枪回到顶部。
- 垂直后沿(Back Porch):电子枪回到顶部的时间段。
- 上边界区域(Top Border):显示图像前的空白区域。
时序如下:
基于上述对VGA时序的详细分析,设计一个VGA控制器主要也就包括行计数器、场计数器,以及行、场同步信号在合适的时刻产生低电平脉冲,以及在显示有效区域将图像数据输出
- 时间节点分析:
行同步有效: 96个clk --> 0,1,2----95 —>95的下一个时钟加入下一步
回扫时间: 40个clk --> 96, 97—135 -->135的下一个时钟加入下一步
左缓冲边界: 8个clk --> 136,137—143 -->143的下一个时钟加入下一步
数据段: 640个clk–> 144, 145–783 -->783的下一个时钟加入下一步
右缓冲边界: 8个clk --> 784, 785–791 -->791的下一个时钟加入下一步
等待回扫: 8个clk --> 792, 793–799 -->799的下一个时钟加入下一步
- 现在对时钟的感知有以下进步:
1. 三个点确定两个线段, 也就是两个时间段
2. 但是现在应该有一个模型在脑子里, 也就是每一个时间点 右方 都有一个自己的时间段
3. 假如从某个时间节点x开始要维持y个时间单元
4. 根据第二点结论, 除原点x外我们需要y-1个时间点, 那么我们可以计算出最后一个时间节点的值为x+y-1, 例如从零时刻开始维持8个时钟周期, 那么最后一个时刻对应的节点为7
端口设计
or
疑问
为什么需要输出那么多端口
因为这个模块不是顶层模块, 所以需要增加很多输出, 作为内部变量方便顶层访问, 比如输出的计数器, 顶层模块访问后可以知道当前显示屏光标的位置,就可以在特定区域实现特定控制
如下为常用的顶层
module VGA (
input i_clk,
input i_arstn,
input [23:0] i_data,
output o_h_sync,
output o_v_sync,
output [23:0] o_data
// 可选隐藏:o_VGA_clk, o_VGA_BLK, o_VGA_ho_cnt, o_VGA_ve_cnt
);
而且, FPGA在连接显示屏前还需要一个DAC芯片(数字转模拟)
如图:顶层模块只需要三个干净的输出,野火电子教程
- 系统上电后,板卡传入系统时钟(sys_clk)和复位信号(sys_rst_n)到顶层模块;
- 系统时钟由顶层模块传入时钟生成模块(clk_gen),分频产生VGA工作时钟(vga_clk),作为图像数据生成模块(vga_pic)和VGA时序控制模块(vga_ctrl)的工作时钟;
- 图像数据生成模块以VGA时序控制模块传入的像素点坐标(pix_x,pix_y)为约束条件,生成待显示彩条图像的色彩信息(pix_data);
- 图像数据生成模块生成的彩条图像色彩信息传入VGA时序控制模块,在模块内部使用使能信号滤除掉非图像显示有效区域的图像数据,产生RGB色彩信息(rgb),在行、场同步信号(hsync、vsync)的同步作用下,将RGB色彩信息扫描显示到VGA显示器,显示出彩条图像
不输出时钟怎么保证电子枪移动速度符合时序
电子枪移动速度的同步原理
(1) 模拟信号的“自时钟”特性
CRT显示器内部的行扫描电路本质上是一个模拟振荡器,其振荡频率由以下因素锁定:
HSYNC信号的频率:显示器会强制将自身的行扫描频率与输入的HSYNC信号同步(类似锁相环PLL的原理)。
例如:在640x480@60Hz模式下,HSYNC频率为31.47kHz,显示器会自动调整电子枪的水平扫描速度以匹配这一频率。
(2) 电子枪的物理运动控制
行扫描电路:CRT内部的行偏转线圈(由锯齿波电流驱动)控制电子枪的水平移动速度。
锯齿波的斜率(即电子枪移动速度)由显示器电路根据HSYNC频率自动调整。
FPGA的RGB数据速率必须与电子枪的物理扫描速度匹配,但这通过以下方式保证:
FPGA以固定的像素时钟(如25.175MHz)生成RGB数据。
由于HSYNC频率是像素时钟的衍生信号*(如每800个像素时钟触发一次HSYNC),因此电子枪的移动速度自然与FPGA数据同步。
VGA转HDMI
https://blog.csdn.net/gslscyx/article/details/137930810
仿真电路图
调用锁相环IP生成25Mhz信号
代码
`timescale 1ns / 1ps
//****************************************VSCODE PLUG-IN**********************************//
//----------------------------------------------------------------------------------------
// IDE : VSCODE
// VSCODE plug-in version: Verilog-Hdl-Format-2.4.20240526
// VSCODE plug-in author : Jiang Percy
//----------------------------------------------------------------------------------------
//****************************************Copyright (c)***********************************//
// Copyright(C) COMPANY_NAME
// All rights reserved
// File name:
// Last modified Date: 2025/05/17 22:22:41
// Last Version: V1.0
// Descriptions:
//----------------------------------------------------------------------------------------
// Created by: USER_NAME
// Created date: 2025/05/17 22:22:41
// Version: V1.0
// TEXT NAME: VGA_top.v
// PATH: D:\vivado\VGA\VGA_top.v
// Descriptions:
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module VGA_top(
input sys_clk ,
input sys_rst_n ,
output o_horizon_syn ,
output o_vertical_syn ,
output [ 23: 0] o_VGA_data
);
//---------------------------------------------------------------------------------------
// PLL锁相环IP调用, 将时钟频率定义为25MHZ
//---------------------------------------------------------------------------------------
wire PPL_clk ;//锁相环
wire locked ;
clk_wiz_0 instance_name
(
// Clock out ports
.clk_out1 ( ),// output clk_out1
.clk_out2 ( ),// output clk_out2
.clk_out3 ( ),// output clk_out3
.clk_out4 (PPL_clk ),// output clk_out4
// Status and control signals
.reset (~sys_rst_n ),// input reset
.locked (locked ),// output locked
// Clock in ports
.clk_in1 (sys_clk) );
//---------------------------------------------------------------------------------------
//
//---------------------------------------------------------------------------------------
wire [ 23: 0] w_VGA_data ;
wire w_VGA_clk ;
wire w_VGA_BLK ;
wire [ 9: 0] w_cnt_horizon ;
wire [ 9: 0] w_cnt_vertical ;
VGA_crl u_VGA(
.i_clk (PPL_clk ),
.i_arstn (sys_rst_n && locked ),
.i_data (w_VGA_data ),
.o_h_sync (o_horizon_syn ),
.o_v_sync (o_vertical_syn ),
.o_data (o_VGA_data ),
.o_VGA_clk (w_VGA_clk ),// 输出一个同步时钟, 基于工程实践, 设置为输入时钟的反时钟更佳
.o_VGA_BLK (w_VGA_BLK ),// 设置一个消隐信号, 似乎用不到
.o_VGA_ho_cnt (w_cnt_horizon ),// 方便顶层读取当前扫描位置
.o_VGA_ve_cnt (w_cnt_vertical ) // 方便顶层读取现在扫描位置
);
VGA_pic u_VGA_pic(
.clk (PPL_clk ),
.rst_n (sys_rst_n && locked ),
.i_cnt_horizon (w_cnt_horizon ),// 从visible像素开始计算
.i_cnt_vertical (w_cnt_vertical ),// 从visible像素开始计算
.o_data (w_VGA_data )
);
endmodule
// verilog 不支持链式条件, 必须分开
`timescale 1ns / 1ps
module VGA_pic(
input clk ,
input rst_n ,
input [ 9: 0] i_cnt_horizon ,//从visible像素开始计算
input [ 9: 0] i_cnt_vertical ,//从visible像素开始计算
output [ 23: 0] o_data
);
//---------------------------------------------------------------------------------------
// 时序参数定义:
parameter VGA_HS_end = 10'd96,//水平同步器维持时间
VGA_H_bach_Porch=10'd40,
VGA_left_border=10'd8,
VGA_H_DATA_time=10'd640,
VGA_right_border=10'd8,
VGA_H_front_Porch=10'd8;
parameter period_horizon = VGA_HS_end+VGA_H_bach_Porch+VGA_left_border+VGA_H_DATA_time+VGA_right_border+VGA_H_front_Porch;
parameter VGA_h_visible_start = VGA_HS_end+VGA_H_bach_Porch+VGA_left_border-1;
parameter VGA_h_visible_end = VGA_h_visible_start+VGA_H_DATA_time;
parameter VGA_VS_end = 10'd2 ,//垂直同步器维持时间
VGA_V_bach_Porch=10'd25,
VGA_top_border=10'd8,
VGA_V_DATA_time=10'd480,
VGA_bottom_border=10'd8,
VGA_V_front_Porch=10'd2;
parameter period_vertical = VGA_VS_end+VGA_V_bach_Porch+VGA_top_border +VGA_V_DATA_time+VGA_bottom_border+VGA_V_front_Porch;
parameter VGA_v_visible_start = VGA_VS_end+VGA_V_bach_Porch+VGA_top_border-1;
parameter VGA_v_visible_end = VGA_v_visible_start+VGA_V_DATA_time;
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
//定义颜色编码
localparam
BLACK = 24'h000000, //黑色
BLUE = 24'h0000FF, //蓝色
RED = 24'hFF0000, //红色
PURPPLE =24'hFF00FF, //紫色
GREEN = 24'h00FF00, //绿色
CYAN = 24'h00FFFF, //青色
YELLOW = 24'hFFFF00, //黄色
WHITE = 24'hFFFFFF; //白色
//定义每个像素块的默认显示颜色值
localparam
R0_C0 = BLACK, //第0行0列像素块
R0_C1 = BLUE, //第0行1列像素块
R1_C0 = RED, //第1行0列像素块
R1_C1 = PURPPLE, //第1行1列像素块
R2_C0 = GREEN, //第2行0列像素块
R2_C1 = CYAN, //第2行1列像素块
R3_C0 = YELLOW, //第3行0列像素块
R3_C1 = WHITE; //第3行1列像素块
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
// 列检测
wire C0_act ;
assign C0_act = (0 <= i_cnt_horizon && i_cnt_horizon<320);
wire C1_act ;
assign C1_act = (320 <= i_cnt_horizon && i_cnt_horizon<640);
// 行检测
wire R1_act,R2_act,R3_act,R0_act ;
assign R0_act = (0<=i_cnt_vertical&& i_cnt_vertical<120);
assign R1_act = (120<=i_cnt_vertical&&i_cnt_vertical<240);
assign R2_act = (240<=i_cnt_vertical&& i_cnt_vertical<360);
assign R3_act = (360<=i_cnt_vertical&& i_cnt_vertical<480);
//---------------------------------------------------------------------------------------
reg [ 23: 0] r_VGA_data ;
always@(*)begin
if (R3_act&C1_act) begin
r_VGA_data = R3_C1;
end
else if (R3_act&C0_act) begin
r_VGA_data = R3_C0;
end
else if (R2_act&C1_act) begin
r_VGA_data = R2_C1;
end
else if (R2_act&C0_act) begin
r_VGA_data = R2_C0;
end
else if (R1_act&C1_act) begin
r_VGA_data = R1_C1;
end
else if (R1_act&C0_act) begin
r_VGA_data = R1_C0;
end
else if (R0_act&C1_act) begin
r_VGA_data = R0_C1;
end
else if (R0_act&C0_act) begin
r_VGA_data = R0_C0;
end
else r_VGA_data =24'd0;
end
assign o_data = r_VGA_data;
endmodule
module VGA_crl (
input i_clk ,
input i_arstn ,
input [ 23: 0] i_data ,
output o_h_sync ,
output o_v_sync ,
output [ 23: 0] o_data ,
output o_VGA_clk ,//输出一个同步时钟, 基于工程实践, 设置为输入时钟的反时钟更佳
output o_VGA_BLK ,//设置一个消隐信号, 似乎用不到
output [ 9: 0] o_VGA_ho_cnt ,//方便顶层读取当前扫描位置
output [ 9: 0] o_VGA_ve_cnt //方便顶层读取现在扫描位置
);
//---------------------------------------------------------------------------------------
// 时序参数定义:
parameter VGA_HS_end = 10'd96,//水平同步器维持时间
VGA_H_bach_Porch=10'd40,
VGA_left_border=10'd8,
VGA_H_DATA_time=10'd640,
VGA_right_border=10'd8,
VGA_H_front_Porch=10'd8;
parameter period_horizon = VGA_HS_end+VGA_H_bach_Porch+VGA_left_border+VGA_H_DATA_time+VGA_right_border+VGA_H_front_Porch;
parameter VGA_h_visible_start = VGA_HS_end+VGA_H_bach_Porch+VGA_left_border-1;
parameter VGA_h_visible_end = VGA_h_visible_start+VGA_H_DATA_time;
parameter VGA_VS_end = 10'd2 ,//垂直同步器维持时间
VGA_V_bach_Porch=10'd25,
VGA_top_border=10'd8,
VGA_V_DATA_time=10'd480,
VGA_bottom_border=10'd8,
VGA_V_front_Porch=10'd2;
parameter period_vertical = VGA_VS_end+VGA_V_bach_Porch+VGA_top_border +VGA_V_DATA_time+VGA_bottom_border+VGA_V_front_Porch;
parameter VGA_v_visible_start = VGA_VS_end+VGA_V_bach_Porch+VGA_top_border-1;
parameter VGA_v_visible_end = VGA_v_visible_start+VGA_V_DATA_time;
//---------------------------------------------------------------------------------------
reg [ 9: 0] r_cnt_h ;// horizon counter
reg [ 9: 0] r_cnt_v ;// vertical counter
always @(posedge i_clk or negedge i_arstn) begin
if (!i_arstn) begin
r_cnt_h <= 0;
end
else begin
if (r_cnt_h == period_horizon-1) begin
r_cnt_h <= 0;
end
else begin
r_cnt_h <= r_cnt_h + 1;
end
end
end
always @(posedge i_clk or negedge i_arstn) begin
if (!i_arstn) begin
r_cnt_v <= 0;
end
else begin
if (r_cnt_h == period_horizon-1) begin
if (r_cnt_v == period_vertical-1) begin
r_cnt_v <= 0;
end
else begin
r_cnt_v <= r_cnt_v + 1;
end
end
else r_cnt_v <= r_cnt_v;
end
end
wire w_VGA_HS ;// 行同步信号
wire w_VGA_VS ;// 场同步信号
assign w_VGA_HS = (r_cnt_h > VGA_HS_end-1);
assign w_VGA_VS = (r_cnt_v > VGA_VS_end-1);
/*
时间节点分析:
行同步有效: 96个clk --> 0,1,2----95 --->95的下一个时钟加入下一步
回扫时间: 40个clk --> 96, 97---135 -->135的下一个时钟加入下一步
左缓冲边界: 8个clk --> 136,137---143 -->143的下一个时钟加入下一步
数据段: 640个clk--> 144, 145--783 -->783的下一个时钟加入下一步
右缓冲边界: 8个clk --> 784, 785--791 -->791的下一个时钟加入下一步
等待回扫: 8个clk --> 792, 793--799 -->799的下一个时钟加入下一步
*/
wire w_dat_act ;// 数据使能信号, 我们只希望在中间输出数据, 因此在这个阶段使能, 注意不输出数据不代表直接关闭电子枪
assign w_dat_act = (VGA_h_visible_start < r_cnt_h && r_cnt_h <=VGA_h_visible_end) && (VGA_v_visible_start < r_cnt_v && r_cnt_v <= VGA_v_visible_end);
//RGB控制
wire [ 23: 0] w_VGA_GRB ;
assign w_VGA_GRB = (w_dat_act)? i_data:24'd0;
assign o_data = w_VGA_GRB;
//同步时钟控制
assign o_VGA_clk = ~i_clk;
//行场同步信号
assign o_h_sync = w_VGA_HS;
assign o_v_sync = w_VGA_VS;
//消隐信号, BLK等同于data_act, 高电平代表着输出数据(visible)的时间锻, 可能是低电平有效, 这里和原理不太一样,原理上消隐信号应该只在等待回扫和回扫时间有效
assign o_VGA_BLK = w_dat_act;
//行场的计数器输出, 方便顶层读取当前位置
assign o_VGA_ho_cnt = (w_dat_act)? (r_cnt_h-VGA_h_visible_start):10'd0;
assign o_VGA_ve_cnt = (w_dat_act)? (r_cnt_v-VGA_v_visible_start):10'd0;
endmodule
总结:VGA看野火电子教程
将VGA控制器分成三个子模块:
- PLL生成固定时钟
- VGA_CRL: 实现VGA协议的基本时序控制, 输出VGA数据以及可视区计数器
- VGA_PIC: 通过读取CRL的可视区计数器, 知道当前可视区像素位置, 将要输出的图像写在这里, 并把图像背后的像素数据发送给CRL, CRL会通过内部指针决定什么时候传递出顶层端口
HDMI
TMDS传输原理
TMDS(Transition Minimized Differential signal),最小化传输差分信号:
HDMI中的TMDS 传输系统分为两个部分:发送端和接收端。 TMDS 发送端收到HDMI 接口传来的表示 RGB 信号的24 位并行数据(TMDS 对每个像素的 RGB 三原色分别按 8bit 编码,即 R信号有 8 位,G 信号有 8 位,B 信号有 8 位),然后对这些数据和时钟信号进行编码和并/串转换,再将表示 3 个 RGB 信号的数据和时钟信号分别分配到独立的传输通道发送出去。接收端接收来自发送端的串行信号,对其进行解码和串/并转换,然后发送到显示器的控制端。与此同时也接收时钟信号,以实现同步。流程框图如下图所示
TMDS通道包括 3 个RGB 数据传输通道和 1 个时钟信号传输通道。每一通道都通过编码算法,将 8 位的视频、音频数据转换成最小化传输、直流平衡的 10 位数据.
第一组中,将像素数据中的蓝色(BLUE)分量和控制信号中的行同步(HSYNC)、场同步(VSYNC) 划分成了一组,经过编码器和串行调制器后输出,名为Channel0。
第二组中,将像素数据中的绿色(GREEN)分量和两个空的控制信号 CTL0、CTL1划分成了一组,经过编码器和串行调制器后输出,名为Channel1。在这一组中,CTL0和CTL1接入的是空信号。
第三组中,将像素数据中的红色(RED)分量和两个空的控制信号CTL2、CTL3划分成了一组,经过编码器和串行调制器后输出,名为Channel2。在这一组中CTL2和CTL3接入的是空信号。
为什么使用TMDS
HDMI接口结构
HDMI的彩条显示是基于VGA彩条显示的基础上的,是在VGA彩条显示工程的基础上修改的得到的。其中改动较大的有两部分:一是时钟生成模块的输出时钟频率和时钟个数做了改动;二是增加了HDMI驱动控制模块hdmi_ctrl。
HDMI_crl: 实现VGA转HDMI
HDMI驱动控制模块hdmi_ctrl是HDMI彩条显示的核心模块,功能是将VGA控制模块传入的行场同步信号、图像信息转换为HDMI能读取的差分信号,实现这一功能的转化,需要对输入的VGA图像信息进行TMDS编码、并行串行转换、单端信号转差分信号、单沿采样转双沿采样。其内部实例化若干子模块,模块及模块简介,如下所示
构成1:TMDS encoder模块
编码方式:
像素数据–>最小传输编码(减少数据跳变)+直流平衡编码(传输1个数等于传输0个数)
控制数据–>查表编码
数据周期:
//使用官方代码
module encode
(
input wire sys_clk ,//时钟信号
input wire sys_rst_n ,//复位信号,低有效
input wire [ 7: 0] data_in ,//输入8bit待编码数据
input wire c0 ,//控制信号c0
input wire c1 ,//控制信号c1
input wire de ,//使能信号
output reg [ 9: 0] data_out //输出编码后的10bit数据
);
//\* Parameter and Internal Signal \//
//parameter define
parameter DATA_OUT0 = 10'b1101010100,
DATA_OUT1 = 10'b0010101011,
DATA_OUT2 = 10'b0101010100,
DATA_OUT3 = 10'b1010101011;
//wire define
wire condition_1 ;//条件1
wire condition_2 ;//条件2
wire condition_3 ;//条件3
wire [ 8: 0] q_m ;//第一阶段转换后的9bit数据
//reg define
reg [ 3: 0] data_in_n1 ;//待编码数据中1的个数
reg [ 7: 0] data_in_reg ;//待编码数据打一拍
reg [ 3: 0] q_m_n1 ;//转换后9bit数据中1的个数
reg [ 3: 0] q_m_n0 ;//转换后9bit数据中0的个数
reg [ 4: 0] cnt ;//视差计数器,0-1个数差别,最高位为符号位
reg de_reg1 ;//使能信号打一拍
reg de_reg2 ;//使能信号打两拍
reg c0_reg1 ;//控制信号c0打一拍
reg c0_reg2 ;//控制信号c0打两拍
reg c1_reg1 ;//控制信号c1打一拍
reg c1_reg2 ;//控制信号c1打两拍
reg [ 8: 0] q_m_reg ;//q_m信号打一拍
//\* Main Code \//
//data_in_n1:待编码数据中1的个数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_in_n1 <= 4'd0;
else
data_in_n1 <= data_in[0] + data_in[1] + data_in[2]+ data_in[3] + data_in[4] + data_in[5]+ data_in[6] + data_in[7];
//data_in_reg:待编码数据打一拍
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_in_reg <= 8'b0;
else
data_in_reg <= data_in;
//condition_1:条件1
assign condition_1 = ((data_in_n1 > 4'd4) || ((data_in_n1 == 4'd4) && (data_in_reg[0] == 1'b1)));
//q_m:第一阶段转换后的9bit数据
assign q_m[0] = data_in_reg[0];
assign q_m[1] = (condition_1) ? (q_m[0] ^~ data_in_reg[1]) : (q_m[0] ^ data_in_reg[1]);
assign q_m[2] = (condition_1) ? (q_m[1] ^~ data_in_reg[2]) : (q_m[1] ^ data_in_reg[2]);
assign q_m[3] = (condition_1) ? (q_m[2] ^~ data_in_reg[3]) : (q_m[2] ^ data_in_reg[3]);
assign q_m[4] = (condition_1) ? (q_m[3] ^~ data_in_reg[4]) : (q_m[3] ^ data_in_reg[4]);
assign q_m[5] = (condition_1) ? (q_m[4] ^~ data_in_reg[5]) : (q_m[4] ^ data_in_reg[5]);
assign q_m[6] = (condition_1) ? (q_m[5] ^~ data_in_reg[6]) : (q_m[5] ^ data_in_reg[6]);
assign q_m[7] = (condition_1) ? (q_m[6] ^~ data_in_reg[7]) : (q_m[6] ^ data_in_reg[7]);
assign q_m[8] = (condition_1) ? 1'b0 : 1'b1;
//q_m_n1:转换后9bit数据中1的个数
//q_m_n0:转换后9bit数据中0的个数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
q_m_n1 <= 4'd0;
q_m_n0 <= 4'd0;
end
else
begin
q_m_n1<=q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];
q_m_n0<=4'd8-(q_m[0]+q_m[1]+q_m[2]+q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]);
end
//condition_2:条件2
assign condition_2 = ((cnt == 5'd0) || (q_m_n1 == q_m_n0));
//condition_3:条件3
assign condition_3 = (((~cnt[4] == 1'b1) && (q_m_n1 > q_m_n0))
|| ((cnt[4] == 1'b1) && (q_m_n0 > q_m_n1)));
//数据打拍,为了各数据同步
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
de_reg1 <= 1'b0;
de_reg2 <= 1'b0;
c0_reg1 <= 1'b0;
c0_reg2 <= 1'b0;
c1_reg1 <= 1'b0;
c1_reg2 <= 1'b0;
q_m_reg <= 9'b0;
end
else
begin
de_reg1 <= de;
de_reg2 <= de_reg1;
c0_reg1 <= c0;
c0_reg2 <= c0_reg1;
c1_reg1 <= c1;
c1_reg2 <= c1_reg1;
q_m_reg <= q_m;
end
//data_out:输出编码后的10bit数据
//cnt:视差计数器,0-1个数差别,最高位为符号位
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
data_out <= 10'b0;
cnt <= 5'b0;
end
else
begin
if(de_reg2 == 1'b1)
begin
if(condition_2 == 1'b1)
begin
data_out[9] <= ~q_m_reg[8];
data_out[8] <= q_m_reg[8];
data_out[7:0]<=(q_m_reg[8])?q_m_reg[7:0] : ~q_m_reg[7:0];
cnt<=(~q_m_reg[8])?(cnt+q_m_n0-q_m_n1):(cnt+q_m_n1-q_m_n0);
end
else
begin
if(condition_3 == 1'b1)
begin
data_out[9] <= 1'b1;
data_out[8] <= q_m_reg[8];
data_out[7:0] <= ~q_m_reg[7:0];
cnt<=cnt+{q_m_reg[8],1'b0}+(q_m_n0-q_m_n1);
end
else
begin
data_out[9] <= 1'b0;
data_out[8] <= q_m_reg[8];
data_out[7:0] <= q_m_reg[7:0];
cnt<= cnt-{~q_m_reg[8],1'b0}+(q_m_n1-q_m_n0);
end
end
end
else
begin
case ({c1_reg2, c0_reg2})
2'b00: data_out <= DATA_OUT0;
2'b01: data_out <= DATA_OUT1;
2'b10: data_out <= DATA_OUT2;
default:data_out <= DATA_OUT3;
endcase
cnt <= 5'b0;
end
end
endmodule
构成2:并转串_差分输出模块
使用编码模块可解决图像数据的编码问题,而并行转串行模块的主要功能就是实现
- 并行串行转换、
- 单端信号转差分信号、
- 单沿采样转双沿采样。
注:传入的时钟信号clk_5x,频率125MHz,为输出串行差分信号ser_data_p、ser_data_n的同步时钟;传入的并行数据信号par_data,同步时钟信号为clk_1x,频率25MHz,未传入本模块。时钟信号clk_1x与clk_5x,时钟频率为5倍关系,因为并行数据信号par_da ta位宽10bit,若转换为串行信号,需要在时钟信号clk_1x的一个时钟周期内完成数据转换,转换后的串行数据信号的同步时钟频率必须为clk_1x的10倍,使用双沿采样则为5倍
调用原语:DDR(时钟沿双边采集)
该核可以实现串行输出D1, D2端数据
时序图:
因此我们可以在五个时钟周期内, 轮流将10位数据发送到D1, D2两端, 从而形成 10位输出的串流
方法: 采用计数器以及多路选择器
回顾:
我在ODDR并转串这里卡了很久
外因:
因为这个原语输出前似乎需要十个clk的准备时间, 而且输出比输入落后一个时钟周期
内因:
对于移位寄存器的使用不熟! 串转并和并转串应该使用移位寄存器, 否则输入帧更新的时候容易获取错误的值, 比如对于4’b0001转 0, 0, 0, 1然后之后是4’b1111转 1 1 1 1, 假如不使用寄存器, 而是直接cnt指向输入, 在输出 0 0 后假如输入更新, 又输出1 1, 这样获得0 0 1 1不属于任何预设输入