module r_g_b( //颜色控制模块
//端口信号:模块的输入输出接口
input clk, //连接至分频时钟,为40MHz
input rst_n, //低电平复位
input en_zx, //红色使能信号
output [:] vga_r, //红色(3位:根据数值的变化,控制颜色的深浅)
output [:] vga_g, //绿色(3位:根据数值的变化,控制颜色的深浅)
output [:] vga_b //蓝色(2位:根据数值的变化,控制颜色的深浅)
);
wire [:] data; //rom取出的值寄存器
reg [:] cnt_ad;//rom地址控制信号
reg [:] cnt_zx;//对应一个数据的8位相应的计数信号
//例化调用rom
rom rom(
.address(cnt_ad),
.clock(clk),
.q(data)
);
//通过计数器取地址0-63
wire en_ad; //地址变化的使能信号:每取完一个8位数据后,地址加1,从而取下一个8位数据
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt_zx <= ;
else if(en_zx)
begin
if(cnt_zx == )
cnt_zx <= ;
else
cnt_zx <= cnt_zx + 'b1;
end
else
cnt_zx <= ;
assign en_ad = (cnt_zx == )?'b1:'b0; //当计数到5时,将信号拉高一个时钟,那么en_ad作为地址计数的使能信号,驱支cnt_ad在下一个时钟,(即cnt_zx==6)时地址加1,
//地址控制信号cnt_ad又在下一个时钟(cnt_zx==7)时传入rom (这是常见的时序问题,值得思考一下)
//在使能信号en_ad为高电平时地址加1
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt_ad <= ;
else if(cnt_ad <= )
begin
if(en_ad)
cnt_ad <= cnt_ad + 'b1;
end
else
cnt_ad <= ;
//在使能区域内,通过查找表的方式,把数据的8位依次从低到高取出
reg vga_r_r;
always@(*)
if(en_zx)
case(cnt_zx)
: vga_r_r <= data[]; //取第0位
: vga_r_r <= data[]; //取第1位
: vga_r_r <= data[]; //取第2位
: vga_r_r <= data[]; //取第3位
: vga_r_r <= data[]; //取第4位
: vga_r_r <= data[]; //取第5位
: vga_r_r <= data[]; //取第6位
: vga_r_r <= data[]; //取第7位
default:;
endcase
//将1位数据通过拼接符号组合成3位数据:增加颜色的深度
assign vga_r = (en_zx)?{{vga_r_r}}:'b000;//在32*16的有效区域内字符用红色表示
assign vga_g = 'b000;
assign vga_b = 'b00;
endmodule
其中“//当计数到5时,将信号拉高一个时钟,那么en_ad作为地址计数的使能信号,驱支cnt_ad在下一个时钟,(即cnt_zx==6)时地址加1,
//地址控制信号cnt_ad又在下一个时钟(cnt_zx==7)时传入rom (这是常见的时序问题,值得思考一下)”这里原来的作者写的有问题 assign的组合逻辑可以完全换成
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt_ad <= ;
else if(cnt_ad <= )
begin
if(cnt_zx == )
cnt_ad <= cnt_ad + 'b1;
end
else
cnt_ad <= ;
这和原来的完全等同,但是cnt_ad加一是发生在cnt_zx==5时,在cnt_zx==6时,cnt_ad已经加一了。已经将加一后的cnt_ad传给rom,在cnt_zx==7时将最新的数据从rom中传递出来,同时在这个时刻vga_r_r 读取data[7]的数据。
附上RTL视图
从RTL视图中可以看出,在zx_cnt==5时完成cnt_ad加一操作采用组合逻辑直接完成,在zx_cnt==6时传递cnt_ad加一后的数值通过一个d触发器将数据传递给rom。在zx_cnt==7时rom将数据传递出来,更新data[7:0]。