1 模拟14位相机时序输出
这是一个模拟相机输出时序的Verilog模块。输入信号为系统时钟信号,系统复位信号。输出信号为像素时钟,14位像素数据,帧有效信号,行有效信号,此时输出的像素在这一帧中的序号。
输出像素数量、行有效信号间隔(每行最后一个像素后多少个时钟周期开始下一行),帧有效信号间隔(例如每帧结束到下一帧开始的时钟周期数)可通过parameter调节。
系统时钟信号直接输出为像素时钟信号,14位像素数据从0开始依次增加,增加到某个可调的阈值后归零继续增加。
1.1 代码
`timescale 1ns / 1ps
module sim_camera_output (
input sys_clk, // 系统时钟信号
input sys_rst_n, // 系统复位信号,低电平有效
output pixel_clk, // 像素时钟
output reg [13:0] pixel_data, // 14位像素数据
output reg frame_valid, // 帧有效信号
output reg line_valid, // 行有效信号
output reg [18:0] pixel_num // 像素序号
);
// 参数定义
parameter PIXEL_COLUMNS = 64; // 每一行的像素数量,列数
parameter PIXEL_ROWS = 51; // 每一帧有多少行,行数
parameter LINE_INTERVAL = 5; // 行有效信号间隔
parameter FRAME_INTERVAL = 10; // 帧有效信号间隔
parameter PIXEL_THRESHOLD = 16383; // 像素数据增加到这个值后归零
reg [18:0] col_counter; // 列计数器
// 像素时钟直接输出为系统时钟
assign pixel_clk = sys_clk;
// 计算此时输出的像素值所在列
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
col_counter <= 0;
end else if (pixel_counter == (PIXEL_COLUMNS + LINE_INTERVAL) * PIXEL_ROWS + FRAME_INTERVAL - 1) begin
col_counter <= 0;
end else if (col_counter == PIXEL_COLUMNS + LINE_INTERVAL - 1) begin
col_counter <= 0;
end else begin
col_counter <= col_counter + 1;
end
end
// 生成行有效信号
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
line_valid <= 0;
end else if (pixel_counter >= (PIXEL_COLUMNS + LINE_INTERVAL) * PIXEL_ROWS - 1) begin
line_valid <= 0;
end else if (col_counter <= PIXEL_COLUMNS - 1) begin
line_valid <= 1;
end else begin
line_valid <= 0;
end
end
reg [18:0] pixel_counter; // 一帧全部像素计数器
// 计算此时一帧中输出像素的数量
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
pixel_counter <= 0;
end else if (pixel_counter == (PIXEL_COLUMNS + LINE_INTERVAL) * PIXEL_ROWS + FRAME_INTERVAL - 1) begin
pixel_counter <= 0;
end else begin
pixel_counter <= pixel_counter + 1;
end
end
// 生成帧有效信号
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
frame_valid <= 0;
end else if (pixel_counter <= (PIXEL_COLUMNS + LINE_INTERVAL) * PIXEL_ROWS - 1) begin
frame_valid <= 1;
end else begin
frame_valid <= 0;
end
end
// 像素数据生成
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
pixel_data <= 0;
end else if (pixel_counter > (PIXEL_COLUMNS + LINE_INTERVAL) * PIXEL_ROWS - 1) begin
pixel_data <= 0;
end else if (pixel_data == PIXEL_THRESHOLD) begin
pixel_data <= 0;
end else if (frame_valid && line_valid) begin
pixel_data <= pixel_data + 1;
end else begin
pixel_data <= pixel_data;
end
end
// 像素序号
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
pixel_num <= 0;
end else if (pixel_counter > (PIXEL_COLUMNS + LINE_INTERVAL) * PIXEL_ROWS - 1) begin
pixel_num <= 0;
end else if (frame_valid && line_valid) begin
pixel_num <= pixel_num + 1;
end else begin
pixel_num <= pixel_num;
end
end
endmodule
1.2 仿真激励代码
`timescale 1ns / 1ps
module camera_output_tb;
reg clk;
reg rest_n;
wire pixel_clk;
wire [13:0] pixel_data;
wire frame_valid;
wire line_valid;
wire [18:0] pixel_num;
// DUT (Device Under Test)
camera_output DUT (
.sys_clk(clk),
.sys_rst_n(rest_n),
.pixel_clk(pixel_clk),
.pixel_data(pixel_data),
.frame_valid(frame_valid),
.line_valid(line_valid),
.pixel_num(pixel_num)
);
// 时钟生成
initial begin
clk = 0;
forever #10 clk = ~clk; // 产生50MHz的时钟信号
end
// 测试序列
initial begin
rest_n = 0; // 初始复位
#100; // 等待100ns以确保时钟稳定
rest_n = 1; // 释放复位
end
endmodule
1.3 测试波形
2 写入一块bram
2.1代码
`timescale 1ns / 1ps
module camera_tobram (
input rest_n,
input pixel_clk,
input [13:0] pixel_data, // 14位像素数据
input frame_valid, // 帧有效信号
input line_valid, // 行有效信号
output reg bram_we, // 写入BRAM的写使能信号,高电平有效
output reg [15:0] bram_data, // 写入BRAM的数据
output reg [18:0] bram_addr // 写入BRAM的地址
);
localparam PIXEL_SIZE = 64 * 51; //输入像素数量
reg [23:0] addr_cnt; //地址计数器
//计算地址,一个像素代表一个地址
always @(posedge pixel_clk or negedge rest_n) begin
if (!rest_n) begin
addr_cnt <= 0;
end else begin
if (frame_valid == 0) begin
addr_cnt <= 0;
end else if (addr_cnt == PIXEL_SIZE - 1) begin
addr_cnt <= 0;
end else if (frame_valid & line_valid) begin
addr_cnt <= addr_cnt + 1;
end else begin
addr_cnt <= addr_cnt;
end
end
end
//对地址进行打一拍处理
always @(posedge pixel_clk or negedge rest_n) begin
if (!rest_n) begin
bram_addr <= 0;
end else begin
bram_addr <= addr_cnt;
end
end
//设置写使能、输出数据
always @(posedge pi