aboutsummaryrefslogtreecommitdiffstats
path: root/display.v
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2003-09-17 05:31:44 +0000
committerH. Peter Anvin <hpa@zytor.com>2003-09-17 05:31:44 +0000
commit2951df3d335d63f60fe40c1b3bf0656f8c37d932 (patch)
tree2899be5e6e3d7eb86af8406952bb92fe5717180d /display.v
parent3b19e346f3e432f3740da6ae9923607cd8520a2f (diff)
downloadabc80-2951df3d335d63f60fe40c1b3bf0656f8c37d932.tar.gz
abc80-2951df3d335d63f60fe40c1b3bf0656f8c37d932.tar.xz
abc80-2951df3d335d63f60fe40c1b3bf0656f8c37d932.zip
Convert display to verilog
Diffstat (limited to 'display.v')
-rw-r--r--display.v316
1 files changed, 316 insertions, 0 deletions
diff --git a/display.v b/display.v
new file mode 100644
index 0000000..39ea78e
--- /dev/null
+++ b/display.v
@@ -0,0 +1,316 @@
+module display (
+ clk,
+ width,
+ a,
+ d,
+ ga,
+ gd,
+ rgb,
+ vsync,
+ hsync
+ );
+ input clk;
+ input width;
+ output [10:0] a;
+ input [7:0] d;
+ output [10:0] ga;
+ input [7:0] gd;
+ output [5:0] rgb;
+ output vsync;
+ output hsync;
+
+// We use the standard VGA "text" monitor timings mode,
+// htime = 31.77 us (31.47 kHz), vtime = 14.27 ms (70 Hz)
+// The standard VGA uses a pixel clock of 25.175 MHz, we use 25 MHz,
+// and round to multiples of 8. The error is about 0.8%, which
+// is far, far less than the margin of error in real systems.
+//
+// This gives us the following timings:
+// Horizontal: 96 pixels (12 char) sync
+// 40 pixels ( 5 char) back porch/border
+// 640 pixels (80 char) graphics
+// 24 pixels ( 3 char) front porch
+// Vertical: 2 lines sync
+// 41 lines back porch/border
+// 384 lines graphics (24 rows @ 16 pixels)
+// 22 lines front porch
+//
+// The DAC has an 8-cycle latency, plus we read each character 8
+// cycles beforehand. Thus we reduce the front porch
+// by 16 and add it to the back porch.
+//
+// In this implementation we start timing with the display area in both
+// cases, *except* that we prefetch by one character, and therefore start
+// 1 character shy of the actual start of display.
+//
+// This gives us 4 clock cycles (@ 25 MHz = 160 ns) to get the character,
+// and an additional 4 clock cycles to get the bit representation of
+// that character.
+//
+// VGA monitors used the sync polarity to determine the mode,
+// especially the desired aspect ratio. Therefore, we want to use -hsync
+// and +vsync, meaning hsync is active low and vsync is active high.
+
+ parameter x_blank = 640+8;
+ parameter x_sync = x_blank+48;
+ parameter x_front = x_sync+96;
+ parameter x_max = x_front+8;
+
+ parameter y_blank = 384;
+ parameter y_sync = y_blank+22;
+ parameter y_front = y_sync+2;
+ parameter y_max = y_front+41;
+
+ parameter hsync_polarity = 1'b0; // -hsync
+ parameter vsync_polarity = 1'b1; // +vsync
+
+ reg [9:0] x; // Horizontal pixel count
+ reg [8:0] y; // Vertical pixel count
+ wire [10:0] a80; // Character row assuming 80 columns
+ reg [5:0] scan_counter; // Counter of total scans (for flashing et al)
+ wire flash_blank; // Should we currently blank flashed text?
+ reg [7:0] pixrow; // One character worth of pixels
+ reg [2:0] fg; // Foreground RGB
+ reg [2:0] bg; // Background RGB
+ reg inverse; // Inverse video
+ reg isgraph; // Graphic mode?
+ reg isgsep; // Separated graphics?
+ reg isflsh; // Flashing?
+ reg [7:0] thischar; // Character code currently processing
+ wire [3:0] xmiddle; // Used in address calculation
+ wire [3:0] ymiddle; // Used in address calculation
+ wire xvideo; // Non-blanked in the x direction
+ wire yvideo; // Non-blanked in the y direction
+
+ // Combinatorial logic
+
+ // Address mapping for 80 characters; for 40 characters
+ // we shift this left by 1
+ assign a80[3:0] = x[6:3];
+ assign xmiddle = { 1'b0, x[9:7] };
+ assign ymiddle = { y[8:7], y[8:7] };
+ assign a80[7:4] = xmiddle+ymiddle;
+ assign a80[10:8] = y[6:4];
+
+ // Final address mapping
+ assign a = width ? a80[10:0] : { 1'b1, a80[10:1] };
+
+ // Character generator address mapping
+ assign ga[10:4] = thischar[6:0];
+ assign ga[3:0] = y[3:0];
+
+ // Video enable signal
+ assign xvideo = ( x < x_blank );
+ assign yvideo = ( y < y_blank );
+
+ // Flashing
+ assign flash_blank = scan_counter[5];
+
+ // Synchronous logic
+
+ always @(posedge clk)
+ begin
+ if ( xvideo & yvideo )
+ if ( pixrow[7] & ~(isflsh & flash_blank) )
+ begin
+ rgb[5] <= fg[2] ^ inverse;
+ rgb[4] <= fg[2] ^ inverse;
+ rgb[3] <= fg[1] ^ inverse;
+ rgb[2] <= fg[1] ^ inverse;
+ rgb[1] <= fg[0] ^ inverse;
+ rgb[0] <= fg[0] ^ inverse;
+ end
+ else
+ begin
+ rgb[5] <= bg[2] ^ inverse;
+ rgb[4] <= bg[2] ^ inverse;
+ rgb[3] <= bg[1] ^ inverse;
+ rgb[2] <= bg[1] ^ inverse;
+ rgb[1] <= bg[0] ^ inverse;
+ rgb[0] <= bg[0] ^ inverse;
+ end
+ else
+ rgb <= 6'b0;
+
+ // Sync pulses
+ vsync <= ( y >= y_sync && y < y_front ) ^ ~vsync_polarity;
+ hsync <= ( x >= x_sync && x < x_front ) ^ ~hsync_polarity;
+
+ // Shift register; may be overridden by the below
+ if ( width | x[0] )
+ pixrow <= { pixrow[6:0], 1'bx };
+
+ // This code is run 8 times per character; regardless of width
+ if ( width | ~x[3] )
+ begin
+ case ( x[2:0] )
+ 3'b011:
+ begin
+ // Load and process character
+ thischar <= d;
+ end
+
+ 3'b111:
+ begin
+ // Load a new pixel row?
+ if ( xvideo )
+ begin
+ if ( thischar[6:5] == 2'b00 )
+ begin
+ // Control character
+ casex ( thischar[4:0] )
+ 5'bx0xxx:
+ begin
+ fg <= thischar[2:0];
+ isgraph <= thischar[4];
+ end
+ 5'b1100x:
+ isflsh <= thischar[0];
+ 5'h19:
+ isgsep <= 1'b0;
+ 5'h1A:
+ isgsep <= 1'b1;
+ 5'h1C:
+ bg <= 3'b000;
+ 5'h1D:
+ begin
+ bg <= fg;
+ fg <= 3'b000;
+ end
+ endcase // casex( thischar[4:0] )
+ end // if ( thischar[6:5] == 2'b00 )
+
+ if ( isgraph & thischar[5] )
+ begin
+ // Generate graphical character
+ case ( y[3:0] )
+ 4'h0, 4'h1, 4'h2, 4'h3:
+ begin
+ pixrow[7] <= thischar[0];
+ pixrow[6] <= thischar[0];
+ pixrow[5] <= thischar[0];
+ pixrow[4] <= thischar[0] & ~isgsep;
+ pixrow[3] <= thischar[1];
+ pixrow[2] <= thischar[1];
+ pixrow[1] <= thischar[1];
+ pixrow[0] <= thischar[1] & ~isgsep;
+ end
+ 4'h4:
+ begin
+ pixrow[7] <= thischar[0] & ~isgsep;
+ pixrow[6] <= thischar[0] & ~isgsep;
+ pixrow[5] <= thischar[0] & ~isgsep;
+ pixrow[4] <= thischar[0] & ~isgsep;
+ pixrow[3] <= thischar[1] & ~isgsep;
+ pixrow[2] <= thischar[1] & ~isgsep;
+ pixrow[1] <= thischar[1] & ~isgsep;
+ pixrow[0] <= thischar[1] & ~isgsep;
+ end
+ 4'h5, 4'h6, 4'h7, 4'h8, 4'h9:
+ begin
+ pixrow[7] <= thischar[2];
+ pixrow[6] <= thischar[2];
+ pixrow[5] <= thischar[2];
+ pixrow[4] <= thischar[2] & ~isgsep;
+ pixrow[3] <= thischar[3];
+ pixrow[2] <= thischar[3];
+ pixrow[1] <= thischar[3];
+ pixrow[0] <= thischar[3] & ~isgsep;
+ end
+ 4'hA:
+ begin
+ pixrow[7] <= thischar[2] & ~isgsep;
+ pixrow[6] <= thischar[2] & ~isgsep;
+ pixrow[5] <= thischar[2] & ~isgsep;
+ pixrow[4] <= thischar[2] & ~isgsep;
+ pixrow[3] <= thischar[3] & ~isgsep;
+ pixrow[2] <= thischar[3] & ~isgsep;
+ pixrow[1] <= thischar[3] & ~isgsep;
+ pixrow[0] <= thischar[3] & ~isgsep;
+ end
+ 4'hB, 4'hC, 4'hD, 4'hE:
+ begin
+ pixrow[7] <= thischar[4];
+ pixrow[6] <= thischar[4];
+ pixrow[5] <= thischar[4];
+ pixrow[4] <= thischar[4] & ~isgsep;
+ pixrow[3] <= thischar[6];
+ pixrow[2] <= thischar[6];
+ pixrow[1] <= thischar[6];
+ pixrow[0] <= thischar[6] & ~isgsep;
+ end
+ 4'hF:
+ begin
+ pixrow[7] <= thischar[4] & ~isgsep;
+ pixrow[6] <= thischar[4] & ~isgsep;
+ pixrow[5] <= thischar[4] & ~isgsep;
+ pixrow[4] <= thischar[4] & ~isgsep;
+ pixrow[3] <= thischar[6] & ~isgsep;
+ pixrow[2] <= thischar[6] & ~isgsep;
+ pixrow[1] <= thischar[6] & ~isgsep;
+ pixrow[0] <= thischar[6] & ~isgsep;
+ end
+ endcase // case( y[3:0] )
+ end // if ( isgraph & thischar[5] )
+ else
+ pixrow <= gd; // Input from character ROM
+ end // if ( xvideo )
+ inverse <= thischar[7];
+ end // case: 3'b111
+ endcase // case( x[2:0] )
+ end // if ( width | ~x[3] )
+
+ // Counters
+ if ( x == x_max-1 )
+ begin
+ x <= 0;
+ fg <= 3'b111; // Default fg is white
+ bg <= 3'b000; // Default bg is black
+ isgraph <= 0; // Not graphic mode
+ inverse <= 0; // Not inverse video
+ isflsh <= 0; // Not flashing
+ isgsep <= 0; // Not separated
+ pixrow <= 8'b0; // Read-ahead spot is blank
+ if ( y == y_max-1 )
+ begin
+ y <= 0;
+ scan_counter <= scan_counter + 1;
+ end
+ else
+ y <= y + 1;
+ end // if ( x == x_max-1 )
+ else
+ x <= x + 1;
+ end // always @ (posedge clk)
+endmodule // display
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \ No newline at end of file