Files
Archie_MiSTer/vidc.v
2019-11-16 02:56:30 +08:00

370 lines
8.7 KiB
Verilog

`timescale 1ns / 1ps
/* vidc.v
Copyright (c) 2012-2014, Stephen J. Leary
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
module vidc #(parameter CLKCPU)
(
input clkcpu, // cpu bus clock domain
input clkpix,
input cepix,
output [1:0] selpix,
// "wishbone" interface
input rst_i,
input vidw, // write to a register.
input [31:0] cpu_dat,
// dma control.
input [31:0] viddat,
input vidak,
output vidrq,
input sndak,
output sndrq,
output flybk,
// video outputs
output hsync, // active low
output vsync, // active low
output [3:0] video_r,
output [3:0] video_g,
output [3:0] video_b,
output video_en,
input ceaud,
output [15:0] audio_l,
output [15:0] audio_r,
output reg audio_sync
);
wire cur_enabled;
reg cur_enabled_r;
wire enabled;
wire border;
// registers
reg [12:0] vidc_palette[0:15]; // palette register.
reg [15:0] vidc_cr; // control register.
reg [12:0] vidc_border; // border register.
reg [12:0] cur_palette[1:3]; // border register.
// audio clock domain signals
wire [7:0] snd_sam_data;
wire snd_sam_en;
// pixel clock domain signals
//delayed enable.
reg enabled_d1;
reg enabled_d2;
reg pix_load;
wire pix_ack;
wire [7:0] pix_data;
reg [2:0] pix_shift_count;
reg [7:0] pix_data_latch = 8'd0;
// cursor pixel clock signals
reg csr_load;
wire csr_ack;
reg [3:0] csr_load_count;
wire [7:0] csr_data;
reg [2:0] csr_shift_count;
reg [7:0] csr_data_latch = 8'd0;
// internal data request lines
wire currq_int;
wire vidrq_int;
assign selpix = vidc_cr[1:0];
vidc_timing TIMING(
.clkcpu ( clkcpu ),
.wr ( vidw ),
.cpu_dat ( cpu_dat ),
.clkvid ( clkpix ),
.cevid ( cepix ),
.rst ( rst_i ),
.o_hsync ( hsync ),
.o_vsync ( vsync ),
.o_flyback ( flybk ),
.o_enabled ( enabled ),
.o_cursor ( cur_enabled ),
.o_border ( border )
);
// this module does the math for a DMA channel
vidc_dmachannel VIDEODMA (
.rst ( flybk | rst_i ),
.clkcpu ( clkcpu ),
.clkdev ( clkpix ),
.cedev ( cepix ),
.cpu_data ( viddat ),
.ak ( vidak ),
.rq ( vidrq_int ),
.stall ( ~hsync ),
.dev_data ( pix_data ),
.dev_ak ( pix_ack )
);
// this module does the math for a DMA channel
vidc_dmachannel #(.FIFO_SIZE(2)) CURSORDMA (
.rst ( flybk | rst_i ),
.clkcpu ( clkcpu ),
.clkdev ( clkpix ),
.cedev ( cepix ),
.cpu_data ( viddat ),
.ak ( vidak ),
.rq ( currq_int ),
.stall ( hsync | vidrq_int ),
.dev_data ( csr_data ),
.dev_ak ( csr_ack )
);
always @(posedge clkcpu) begin
reg old_en;
old_en <= snd_sam_en;
audio_sync <= ~old_en & snd_sam_en;
end
// this module does the math for a DMA channel
vidc_dmachannel SOUNDDMA (
.rst ( rst_i ),
.clkcpu ( clkcpu ),
.clkdev ( clkpix ),
.cedev ( ceaud ),
.cpu_data ( viddat ),
.ak ( sndak ),
.rq ( sndrq ),
.dev_data ( snd_sam_data ),
.dev_ak ( snd_sam_en )
);
vidc_audio AUDIOMIXER(
.cpu_clk ( clkcpu ),
.cpu_wr ( vidw ),
.cpu_data ( cpu_dat ),
.aud_clk ( clkpix ),
.aud_ce ( ceaud ),
.aud_rst ( rst_i ),
.aud_data ( snd_sam_data ),
.aud_en ( snd_sam_en ),
.aud_right ( audio_r ),
.aud_left ( audio_l )
);
integer c;
initial begin
// clear the palette.
for (c = 0; c < 16; c = c + 1) begin
vidc_palette[c]= 13'd0;
end
vidc_cr = 16'hFFF0;
pix_shift_count = 3'd0;
pix_data_latch = 8'd0;
pix_load = 1'b0;
csr_shift_count = 3'd0;
csr_data_latch = 8'd0;
csr_load_count = 'd0;
csr_load = 1'b0;
end
localparam VIDEO_PALETTE = 6'b00xxxx;
localparam VIDEO_CONTROL = 6'b111000;
localparam VIDEO_BORDER = 6'b010000;
localparam CURSOR_PALETTE = 6'b0100xx;
// DMA interface control
// this is in the cpu clock domain.
always @(posedge clkcpu) begin
// register write control.
if (vidw == 1'b1) begin
casex (cpu_dat[31:26])
VIDEO_PALETTE: begin // palette registers. 00-3CH
vidc_palette[cpu_dat[29:26]] <= cpu_dat[12:0];
end
CURSOR_PALETTE: begin // cursor palette
if (cpu_dat[27:26] == 2'b00) begin
vidc_border <= cpu_dat[12:0];
end else begin
cur_palette[cpu_dat[27:26]] <= cpu_dat[12:0];
end
end
VIDEO_CONTROL: begin // control register.
vidc_cr <= cpu_dat[15:0];
end
endcase
end
end
// pixel clock domain logic.
// this simulates the DAC.
always @(posedge clkpix) begin
if(cepix) begin
cur_enabled_r <= cur_enabled;
pix_load <= pix_ack;
csr_load <= csr_ack;
enabled_d1 <= enabled;
enabled_d2 <= enabled_d1;
if (flybk == 1'b1) begin
pix_data_latch <= 8'd0;
end
if (hsync == 1'b0) begin
pix_shift_count <= 3'b111;
csr_shift_count <= 3'b111;
csr_load_count <= 'd0;
end else if (enabled == 1'b1) begin
case ({vidc_cr[3:2]})
2'b00: begin
pix_data_latch <= {1'b0, pix_data_latch[7:1]};
pix_shift_count <= pix_shift_count + 3'd1;
end
2'b01: begin
pix_data_latch <= {2'b00, pix_data_latch[7:2]};
pix_shift_count <= pix_shift_count + 3'd2;
end
2'b10: begin
pix_data_latch <= {4'b0000, pix_data_latch[7:4]};
pix_shift_count <= pix_shift_count + 3'd4;
end
default: begin
pix_data_latch <= 8'd0;
end
endcase
end
if (cur_enabled_r) begin
csr_data_latch <= {2'b00, csr_data_latch[7:2]};
csr_shift_count <= csr_shift_count + 3'd2;
end
if (pix_load == 1'b1) begin
pix_data_latch <= pix_data;
end
if (csr_load) begin
csr_load_count <= csr_load_count + 1'd1;
csr_data_latch <= csr_data;
end
end
end
// eqn is CE + DE + ABC + BCD (where {E,D} = {vidc_cr[3:2]} and {C,B,A} = pix_shift_count)
assign pix_ack = hsync & enabled & ((pix_shift_count[2] & vidc_cr[3]) | ( vidc_cr[2] & vidc_cr[3]) | (pix_shift_count[0] & pix_shift_count[1] & pix_shift_count[2]) | (pix_shift_count[1] & pix_shift_count[2] & vidc_cr[2]));
assign csr_ack = cur_enabled & (csr_shift_count[2] & csr_shift_count[0] ) & ~csr_load & ~csr_load_count[3];
// TODO: fix 8 bits per pixel colours.
wire [3:0] pix_lookup = vidc_cr[3:2] == 2'b00 ? {3'd0, pix_data_latch[0]} :
vidc_cr[3:2] == 2'b01 ? {2'd0, pix_data_latch[1:0]} : pix_data_latch[3:0];
wire [1:0] csr_lookup = csr_data_latch[1:0];
wire [12:0] vidc_colour = cur_enabled & (csr_lookup != 2'd0) ? cur_palette[csr_lookup] :
enabled_d2 ? vidc_palette[pix_lookup] :
border ? vidc_border :
13'd0;
// render a hicolour pixel if in hicolour mode, enabled and the cursor isnt being displayed.
wire hicolour = (vidc_cr[3:2] == 2'b11) & enabled_d2 & !(cur_enabled & (csr_lookup != 2'd0));
assign video_r[3] = hicolour ? pix_data_latch[4] : vidc_colour[3];
assign video_r[2:0] = vidc_colour[2:0];
assign video_g[3:2] = hicolour ? pix_data_latch[6:5] : vidc_colour[7:6];
assign video_g[1:0] = vidc_colour[5:4];
assign video_b[3] = hicolour ? pix_data_latch[7] : vidc_colour[11];
assign video_b[2:0] = vidc_colour[10:8];
assign video_en = enabled;
// two dma channels share the vidrq.
assign vidrq = vidrq_int | currq_int;
endmodule