Normalize segment data and pass everything through deflicker and vsync

This commit is contained in:
Adam Gastineau
2023-06-21 11:50:57 -07:00
parent 04c044cb89
commit 797596ae25
4 changed files with 123 additions and 113 deletions

View File

@@ -7,6 +7,7 @@ set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "input_config.sv"]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "video/counts.sv"]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "video/lcd.sv"]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "video/normalize.sv"]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "video/rgb_controller.sv"]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "video/segments.sv"]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "video/video.sv"]

View File

@@ -26,34 +26,48 @@ module lcd (
output wire segment_en
);
// Segments, over all H's, as last seen
reg [15:0] segment_a[4];
reg [15:0] segment_b[4];
reg [1:0] segment_bs;
reg [15:0] cache_segment_a[4];
reg [15:0] cache_segment_b[4];
reg [1:0] cache_segment_bs;
reg [3:0] w_prime[9];
reg [3:0] w_main[9];
reg [3:0] cache_w_prime[9];
reg [3:0] cache_w_main[9];
reg [4:0] decay_w_prime[9][4];
reg [4:0] decay_w_main[9][4];
reg prev_divider_1khz = 0;
// Comb
reg [1:0] current_h_index;
reg prev_vblank = 0;
localparam DECAY_MAX = 5'h1F;
localparam DECAY_MIN_DISPLAY = 5'h10;
segments segments (
localparam MAX_X_SEGMENT = 9;
localparam MAX_Y_SEGMENT = 16;
localparam MAX_Z_SEGMENT = 4;
wire [MAX_Z_SEGMENT-1:0] raw_segments[MAX_X_SEGMENT][MAX_Y_SEGMENT];
reg [MAX_Z_SEGMENT-1:0] decayed_segments[MAX_X_SEGMENT][MAX_Y_SEGMENT];
reg [MAX_Z_SEGMENT-1:0] vsync_segments[MAX_X_SEGMENT][MAX_Y_SEGMENT];
reg [4:0] segment_current_decays[MAX_X_SEGMENT][MAX_Y_SEGMENT][MAX_Z_SEGMENT-1:0];
reg prev_divider_1khz = 0;
reg prev_vblank = 0;
normalize #(
.MAX_X_SEGMENT(MAX_X_SEGMENT),
.MAX_Y_SEGMENT(MAX_Y_SEGMENT),
.MAX_Z_SEGMENT(MAX_Z_SEGMENT)
) normalize (
.clk(clk),
.cpu_id(cpu_id),
.current_segment_a (current_segment_a),
.current_segment_b (current_segment_b),
.current_segment_bs(current_segment_bs),
.current_w_prime(current_w_prime),
.current_w_main (current_w_main),
.output_lcd_h_index(output_lcd_h_index),
.segments(raw_segments)
);
segments #(
.MAX_X_SEGMENT(MAX_X_SEGMENT),
.MAX_Y_SEGMENT(MAX_Y_SEGMENT),
.MAX_Z_SEGMENT(MAX_Z_SEGMENT)
) lcd_segments (
.clk(clk),
.cpu_id(cpu_id),
@@ -61,13 +75,7 @@ module lcd (
.mask_data_wr(mask_data_wr),
.mask_data(mask_data),
// Segments
.cache_segment_a (cache_segment_a),
.cache_segment_b (cache_segment_b),
.cache_segment_bs(cache_segment_bs),
.cache_w_prime(cache_w_prime),
.cache_w_main (cache_w_main),
.segments(vsync_segments),
.vblank_int(vblank_int),
.hblank_int(hblank_int),
@@ -78,67 +86,39 @@ module lcd (
);
always @(posedge clk) begin
int i;
int j;
int x, y, z;
prev_vblank <= vblank_int;
prev_divider_1khz <= divider_1khz;
// TODO: This is very similar to the logic already in `ram.sv`
segment_a[output_lcd_h_index] <= current_segment_a;
segment_b[output_lcd_h_index] <= current_segment_b;
segment_bs[output_lcd_h_index] <= current_segment_bs;
if (output_lcd_h_index[0]) begin
// W'
w_prime <= current_w_prime;
end else begin
// W
w_main <= current_w_main;
end
// Pass raw segments through deflicker stage
if (divider_1khz && ~prev_divider_1khz) begin
for (i = 0; i < 9; i += 1) begin
for (j = 0; j < 4; j += 1) begin
// W'
// Modify decay
if (w_prime[i][j] && decay_w_prime[i][j] < DECAY_MAX) begin
// Segment is on, and decay isn't max
// Increment decay
decay_w_prime[i][j] <= decay_w_prime[i][j] + 5'h1;
end else if (~w_prime[i][j] && decay_w_prime[i][j] > 5'h0) begin
// Segment is off, and decay isn't min
// Decrement decay
decay_w_prime[i][j] <= decay_w_prime[i][j] - 5'h1;
for (x = 0; x < MAX_X_SEGMENT; x += 1) begin
for (y = 0; y < MAX_Y_SEGMENT; y += 1) begin
for (z = 0; z < MAX_Z_SEGMENT; z += 1) begin
reg [4:0] current_decay;
current_decay = segment_current_decays[x][y][z];
if (raw_segments[x][y][z] && current_decay < DECAY_MAX) begin
// Segment is on, and decay isn't max
// Increment decay
segment_current_decays[x][y][z] <= current_decay + 5'h1;
end else if (~raw_segments[x][y][z] && current_decay > 5'h0) begin
// Segment is off, and decay isn't min
// Decrement decay
segment_current_decays[x][y][z] <= current_decay - 5'h1;
end
// Update segment array (an iteration delayed)
decayed_segments[x][y][z] <= current_decay > DECAY_MIN_DISPLAY;
end
// Update segment array (an iteration delayed)
cache_w_prime[i][j] <= decay_w_prime[i][j] > DECAY_MIN_DISPLAY;
// W
if (w_main[i][j] && decay_w_main[i][j] < DECAY_MAX) begin
decay_w_main[i][j] <= decay_w_main[i][j] + 5'h1;
end else if (~w_main[i][j] && decay_w_main[i][j] > 5'h0) begin
decay_w_main[i][j] <= decay_w_main[i][j] - 5'h1;
end
cache_w_main[i][j] <= decay_w_main[i][j] > DECAY_MIN_DISPLAY;
end
end
end
// Pass deflickered segment through vblank buffer stage
if (vblank_int && ~prev_vblank) begin
cache_segment_a[0] <= segment_a[0];
cache_segment_a[1] <= segment_a[1];
cache_segment_a[2] <= segment_a[2];
cache_segment_a[3] <= segment_a[3];
cache_segment_b[0] <= segment_b[0];
cache_segment_b[1] <= segment_b[1];
cache_segment_b[2] <= segment_b[2];
cache_segment_b[3] <= segment_b[3];
cache_segment_bs <= segment_bs;
vsync_segments <= decayed_segments;
end
end
endmodule

53
rtl/video/normalize.sv Normal file
View File

@@ -0,0 +1,53 @@
module normalize #(
parameter MAX_X_SEGMENT = 9,
parameter MAX_Y_SEGMENT = 16,
parameter MAX_Z_SEGMENT = 4
) (
input wire clk,
input wire [3:0] cpu_id,
input wire [15:0] current_segment_a,
input wire [15:0] current_segment_b,
input wire current_segment_bs,
input wire [3:0] current_w_prime[9],
input wire [3:0] current_w_main [9],
input wire [1:0] output_lcd_h_index,
// Z is used for one hot bit selection, hence the width
output reg [MAX_Z_SEGMENT-1:0] segments[MAX_X_SEGMENT][MAX_Y_SEGMENT]
);
always @(posedge clk) begin
int x, y, z;
case (cpu_id)
4: begin
// SM5a
for (x = 0; x < MAX_X_SEGMENT; x += 1) begin
// Only 4 Y segments
for (y = 0; y < 4; y += 1) begin
if (output_lcd_h_index[0]) begin
segments[x][y][0] <= current_w_main[x][y];
end else begin
segments[x][y][1] <= current_w_prime[x][y];
end
end
end
end
default: begin
// SM510
for (y = 0; y < MAX_Y_SEGMENT; y += 1) begin
segments[0][y][output_lcd_h_index] <= current_segment_a[y];
segments[1][y][output_lcd_h_index] <= current_segment_b[y];
// BS is a special case as a single bit, and is set for all Y
segments[2][y][output_lcd_h_index] <= current_segment_bs;
end
end
endcase
end
endmodule

View File

@@ -1,4 +1,8 @@
module segments (
module segments #(
parameter MAX_X_SEGMENT = 9,
parameter MAX_Y_SEGMENT = 16,
parameter MAX_Z_SEGMENT = 4
) (
input wire clk,
input wire [3:0] cpu_id,
@@ -6,13 +10,7 @@ module segments (
input wire mask_data_wr,
input wire [15:0] mask_data,
// Segments
input wire [15:0] cache_segment_a [4],
input wire [15:0] cache_segment_b [4],
input wire [ 1:0] cache_segment_bs,
input wire [3:0] cache_w_prime[9],
input wire [3:0] cache_w_main [9],
input wire [MAX_Z_SEGMENT-1:0] segments[MAX_X_SEGMENT][MAX_Y_SEGMENT],
// Video counters
input wire vblank_int,
@@ -53,29 +51,7 @@ module segments (
segment_en = 0;
if (has_segment) begin
case (cpu_id)
4: begin
// SM5a
if (segment_row == 2'h1) begin
// W'
segment_en = cache_w_prime[segment_line_select][segment_column];
end else begin
// W
segment_en = cache_w_main[segment_line_select][segment_column];
end
end
default: begin
// SM510
case (segment_line_select)
4'h0: segment_en = cache_segment_a[segment_row][segment_column];
4'h1: segment_en = cache_segment_b[segment_row][segment_column];
4'h2: segment_en = cache_segment_bs[segment_row];
default: begin
// TODO: What happens in these cases?
end
endcase
end
endcase
segment_en = segments[segment_line_select][segment_column][segment_row];
end
end
endmodule