mirror of
https://github.com/nippur72/Apple1_MiST.git
synced 2025-02-28 18:29:27 +00:00
455 lines
12 KiB
Systemverilog
455 lines
12 KiB
Systemverilog
//
|
|
//
|
|
// Copyright (c) 2012-2013 Ludvig Strigeus
|
|
// Copyright (c) 2017 Sorgelig
|
|
//
|
|
// This program is GPL Licensed. See COPYING for the full license.
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// synopsys translate_off
|
|
`timescale 1 ps / 1 ps
|
|
// synopsys translate_on
|
|
|
|
`define BITS_TO_FIT(N) ( \
|
|
N <= 2 ? 0 : \
|
|
N <= 4 ? 1 : \
|
|
N <= 8 ? 2 : \
|
|
N <= 16 ? 3 : \
|
|
N <= 32 ? 4 : \
|
|
N <= 64 ? 5 : \
|
|
N <= 128 ? 6 : \
|
|
N <= 256 ? 7 : \
|
|
N <= 512 ? 8 : \
|
|
N <=1024 ? 9 : 10 )
|
|
|
|
module hq2x_in #(parameter LENGTH, parameter DWIDTH)
|
|
(
|
|
input clk,
|
|
|
|
input [AWIDTH:0] rdaddr,
|
|
input rdbuf,
|
|
output[DWIDTH:0] q,
|
|
|
|
input [AWIDTH:0] wraddr,
|
|
input wrbuf,
|
|
input [DWIDTH:0] data,
|
|
input wren
|
|
);
|
|
|
|
localparam AWIDTH = `BITS_TO_FIT(LENGTH);
|
|
wire [DWIDTH:0] out[2];
|
|
assign q = out[rdbuf];
|
|
|
|
hq2x_buf #(.NUMWORDS(LENGTH), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf0(clk,data,rdaddr,wraddr,wren && (wrbuf == 0),out[0]);
|
|
hq2x_buf #(.NUMWORDS(LENGTH), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf1(clk,data,rdaddr,wraddr,wren && (wrbuf == 1),out[1]);
|
|
endmodule
|
|
|
|
|
|
module hq2x_out #(parameter LENGTH, parameter DWIDTH)
|
|
(
|
|
input clk,
|
|
|
|
input [AWIDTH:0] rdaddr,
|
|
input [1:0] rdbuf,
|
|
output[DWIDTH:0] q,
|
|
|
|
input [AWIDTH:0] wraddr,
|
|
input [1:0] wrbuf,
|
|
input [DWIDTH:0] data,
|
|
input wren
|
|
);
|
|
|
|
localparam AWIDTH = `BITS_TO_FIT(LENGTH*2);
|
|
wire [DWIDTH:0] out[4];
|
|
assign q = out[rdbuf];
|
|
|
|
hq2x_buf #(.NUMWORDS(LENGTH*2), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf0(clk,data,rdaddr,wraddr,wren && (wrbuf == 0),out[0]);
|
|
hq2x_buf #(.NUMWORDS(LENGTH*2), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf1(clk,data,rdaddr,wraddr,wren && (wrbuf == 1),out[1]);
|
|
hq2x_buf #(.NUMWORDS(LENGTH*2), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf2(clk,data,rdaddr,wraddr,wren && (wrbuf == 2),out[2]);
|
|
hq2x_buf #(.NUMWORDS(LENGTH*2), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf3(clk,data,rdaddr,wraddr,wren && (wrbuf == 3),out[3]);
|
|
endmodule
|
|
|
|
|
|
module hq2x_buf #(parameter NUMWORDS, parameter AWIDTH, parameter DWIDTH)
|
|
(
|
|
input clock,
|
|
input [DWIDTH:0] data,
|
|
input [AWIDTH:0] rdaddress,
|
|
input [AWIDTH:0] wraddress,
|
|
input wren,
|
|
output [DWIDTH:0] q
|
|
);
|
|
|
|
altsyncram altsyncram_component (
|
|
.address_a (wraddress),
|
|
.clock0 (clock),
|
|
.data_a (data),
|
|
.wren_a (wren),
|
|
.address_b (rdaddress),
|
|
.q_b(q),
|
|
.aclr0 (1'b0),
|
|
.aclr1 (1'b0),
|
|
.addressstall_a (1'b0),
|
|
.addressstall_b (1'b0),
|
|
.byteena_a (1'b1),
|
|
.byteena_b (1'b1),
|
|
.clock1 (1'b1),
|
|
.clocken0 (1'b1),
|
|
.clocken1 (1'b1),
|
|
.clocken2 (1'b1),
|
|
.clocken3 (1'b1),
|
|
.data_b ({(DWIDTH+1){1'b1}}),
|
|
.eccstatus (),
|
|
.q_a (),
|
|
.rden_a (1'b1),
|
|
.rden_b (1'b1),
|
|
.wren_b (1'b0));
|
|
defparam
|
|
altsyncram_component.address_aclr_b = "NONE",
|
|
altsyncram_component.address_reg_b = "CLOCK0",
|
|
altsyncram_component.clock_enable_input_a = "BYPASS",
|
|
altsyncram_component.clock_enable_input_b = "BYPASS",
|
|
altsyncram_component.clock_enable_output_b = "BYPASS",
|
|
altsyncram_component.intended_device_family = "Cyclone III",
|
|
altsyncram_component.lpm_type = "altsyncram",
|
|
altsyncram_component.numwords_a = NUMWORDS,
|
|
altsyncram_component.numwords_b = NUMWORDS,
|
|
altsyncram_component.operation_mode = "DUAL_PORT",
|
|
altsyncram_component.outdata_aclr_b = "NONE",
|
|
altsyncram_component.outdata_reg_b = "UNREGISTERED",
|
|
altsyncram_component.power_up_uninitialized = "FALSE",
|
|
altsyncram_component.read_during_write_mode_mixed_ports = "DONT_CARE",
|
|
altsyncram_component.widthad_a = AWIDTH+1,
|
|
altsyncram_component.widthad_b = AWIDTH+1,
|
|
altsyncram_component.width_a = DWIDTH+1,
|
|
altsyncram_component.width_b = DWIDTH+1,
|
|
altsyncram_component.width_byteena_a = 1;
|
|
|
|
endmodule
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
module DiffCheck
|
|
(
|
|
input [17:0] rgb1,
|
|
input [17:0] rgb2,
|
|
output result
|
|
);
|
|
|
|
wire [5:0] r = rgb1[5:1] - rgb2[5:1];
|
|
wire [5:0] g = rgb1[11:7] - rgb2[11:7];
|
|
wire [5:0] b = rgb1[17:13] - rgb2[17:13];
|
|
wire [6:0] t = $signed(r) + $signed(b);
|
|
wire [6:0] gx = {g[5], g};
|
|
wire [7:0] y = $signed(t) + $signed(gx);
|
|
wire [6:0] u = $signed(r) - $signed(b);
|
|
wire [7:0] v = $signed({g, 1'b0}) - $signed(t);
|
|
|
|
// if y is inside (-24..24)
|
|
wire y_inside = (y < 8'h18 || y >= 8'he8);
|
|
|
|
// if u is inside (-4, 4)
|
|
wire u_inside = (u < 7'h4 || u >= 7'h7c);
|
|
|
|
// if v is inside (-6, 6)
|
|
wire v_inside = (v < 8'h6 || v >= 8'hfA);
|
|
assign result = !(y_inside && u_inside && v_inside);
|
|
endmodule
|
|
|
|
module InnerBlend
|
|
(
|
|
input [8:0] Op,
|
|
input [5:0] A,
|
|
input [5:0] B,
|
|
input [5:0] C,
|
|
output [5:0] O
|
|
);
|
|
|
|
function [8:0] mul6x3;
|
|
input [5:0] op1;
|
|
input [2:0] op2;
|
|
begin
|
|
mul6x3 = 9'd0;
|
|
if(op2[0]) mul6x3 = mul6x3 + op1;
|
|
if(op2[1]) mul6x3 = mul6x3 + {op1, 1'b0};
|
|
if(op2[2]) mul6x3 = mul6x3 + {op1, 2'b00};
|
|
end
|
|
endfunction
|
|
|
|
wire OpOnes = Op[4];
|
|
wire [8:0] Amul = mul6x3(A, Op[7:5]);
|
|
wire [8:0] Bmul = mul6x3(B, {Op[3:2], 1'b0});
|
|
wire [8:0] Cmul = mul6x3(C, {Op[1:0], 1'b0});
|
|
wire [8:0] At = Amul;
|
|
wire [8:0] Bt = (OpOnes == 0) ? Bmul : {3'b0, B};
|
|
wire [8:0] Ct = (OpOnes == 0) ? Cmul : {3'b0, C};
|
|
wire [9:0] Res = {At, 1'b0} + Bt + Ct;
|
|
assign O = Op[8] ? A : Res[9:4];
|
|
endmodule
|
|
|
|
module Blend
|
|
(
|
|
input [5:0] rule,
|
|
input disable_hq2x,
|
|
input [17:0] E,
|
|
input [17:0] A,
|
|
input [17:0] B,
|
|
input [17:0] D,
|
|
input [17:0] F,
|
|
input [17:0] H,
|
|
output [17:0] Result
|
|
);
|
|
|
|
reg [1:0] input_ctrl;
|
|
reg [8:0] op;
|
|
localparam BLEND0 = 9'b1_xxx_x_xx_xx; // 0: A
|
|
localparam BLEND1 = 9'b0_110_0_10_00; // 1: (A * 12 + B * 4) >> 4
|
|
localparam BLEND2 = 9'b0_100_0_10_10; // 2: (A * 8 + B * 4 + C * 4) >> 4
|
|
localparam BLEND3 = 9'b0_101_0_10_01; // 3: (A * 10 + B * 4 + C * 2) >> 4
|
|
localparam BLEND4 = 9'b0_110_0_01_01; // 4: (A * 12 + B * 2 + C * 2) >> 4
|
|
localparam BLEND5 = 9'b0_010_0_11_11; // 5: (A * 4 + (B + C) * 6) >> 4
|
|
localparam BLEND6 = 9'b0_111_1_xx_xx; // 6: (A * 14 + B + C) >> 4
|
|
localparam AB = 2'b00;
|
|
localparam AD = 2'b01;
|
|
localparam DB = 2'b10;
|
|
localparam BD = 2'b11;
|
|
wire is_diff;
|
|
DiffCheck diff_checker(rule[1] ? B : H, rule[0] ? D : F, is_diff);
|
|
|
|
always @* begin
|
|
case({!is_diff, rule[5:2]})
|
|
1,17: {op, input_ctrl} = {BLEND1, AB};
|
|
2,18: {op, input_ctrl} = {BLEND1, DB};
|
|
3,19: {op, input_ctrl} = {BLEND1, BD};
|
|
4,20: {op, input_ctrl} = {BLEND2, DB};
|
|
5,21: {op, input_ctrl} = {BLEND2, AB};
|
|
6,22: {op, input_ctrl} = {BLEND2, AD};
|
|
|
|
8: {op, input_ctrl} = {BLEND0, 2'bxx};
|
|
9: {op, input_ctrl} = {BLEND0, 2'bxx};
|
|
10: {op, input_ctrl} = {BLEND0, 2'bxx};
|
|
11: {op, input_ctrl} = {BLEND1, AB};
|
|
12: {op, input_ctrl} = {BLEND1, AB};
|
|
13: {op, input_ctrl} = {BLEND1, AB};
|
|
14: {op, input_ctrl} = {BLEND1, DB};
|
|
15: {op, input_ctrl} = {BLEND1, BD};
|
|
|
|
24: {op, input_ctrl} = {BLEND2, DB};
|
|
25: {op, input_ctrl} = {BLEND5, DB};
|
|
26: {op, input_ctrl} = {BLEND6, DB};
|
|
27: {op, input_ctrl} = {BLEND2, DB};
|
|
28: {op, input_ctrl} = {BLEND4, DB};
|
|
29: {op, input_ctrl} = {BLEND5, DB};
|
|
30: {op, input_ctrl} = {BLEND3, BD};
|
|
31: {op, input_ctrl} = {BLEND3, DB};
|
|
default: {op, input_ctrl} = 11'bx;
|
|
endcase
|
|
|
|
// Setting op[8] effectively disables HQ2X because blend will always return E.
|
|
if (disable_hq2x) op[8] = 1;
|
|
end
|
|
|
|
// Generate inputs to the inner blender. Valid combinations.
|
|
// 00: E A B
|
|
// 01: E A D
|
|
// 10: E D B
|
|
// 11: E B D
|
|
wire [17:0] Input1 = E;
|
|
wire [17:0] Input2 = !input_ctrl[1] ? A :
|
|
!input_ctrl[0] ? D : B;
|
|
|
|
wire [17:0] Input3 = !input_ctrl[0] ? B : D;
|
|
InnerBlend inner_blend1(op, Input1[5:0], Input2[5:0], Input3[5:0], Result[5:0]);
|
|
InnerBlend inner_blend2(op, Input1[11:6], Input2[11:6], Input3[11:6], Result[11:6]);
|
|
InnerBlend inner_blend3(op, Input1[17:12], Input2[17:12], Input3[17:12], Result[17:12]);
|
|
endmodule
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
module Hq2x #(parameter LENGTH, parameter HALF_DEPTH)
|
|
(
|
|
input clk,
|
|
input ce_x4,
|
|
input [DWIDTH:0] inputpixel,
|
|
input mono,
|
|
input disable_hq2x,
|
|
input reset_frame,
|
|
input reset_line,
|
|
input [1:0] read_y,
|
|
input [AWIDTH+1:0] read_x,
|
|
output [DWIDTH:0] outpixel
|
|
);
|
|
|
|
|
|
localparam AWIDTH = `BITS_TO_FIT(LENGTH);
|
|
localparam DWIDTH = HALF_DEPTH ? 8 : 17;
|
|
|
|
wire [5:0] hqTable[256] = '{
|
|
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 47, 35, 23, 15, 55, 39,
|
|
19, 19, 26, 58, 19, 19, 26, 58, 23, 15, 35, 35, 23, 15, 7, 35,
|
|
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 55, 39, 23, 15, 51, 43,
|
|
19, 19, 26, 58, 19, 19, 26, 58, 23, 15, 51, 35, 23, 15, 7, 43,
|
|
19, 19, 26, 11, 19, 19, 26, 11, 23, 61, 35, 35, 23, 61, 51, 35,
|
|
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 51, 35,
|
|
19, 19, 26, 11, 19, 19, 26, 11, 23, 61, 7, 35, 23, 61, 7, 43,
|
|
19, 19, 26, 11, 19, 19, 26, 58, 23, 15, 51, 35, 23, 61, 7, 43,
|
|
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 47, 35, 23, 15, 55, 39,
|
|
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 51, 35,
|
|
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 55, 39, 23, 15, 51, 43,
|
|
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 39, 23, 15, 7, 43,
|
|
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 51, 39,
|
|
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 7, 35,
|
|
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 7, 43,
|
|
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 7, 35, 23, 15, 7, 43
|
|
};
|
|
|
|
reg [17:0] Prev0, Prev1, Prev2, Curr0, Curr1, Next0, Next1, Next2;
|
|
reg [17:0] A, B, D, F, G, H;
|
|
reg [7:0] pattern, nextpatt;
|
|
reg [1:0] i;
|
|
reg [7:0] y;
|
|
|
|
wire curbuf = y[0];
|
|
reg prevbuf = 0;
|
|
wire iobuf = !curbuf;
|
|
|
|
wire diff0, diff1;
|
|
DiffCheck diffcheck0(Curr1, (i == 0) ? Prev0 : (i == 1) ? Curr0 : (i == 2) ? Prev2 : Next1, diff0);
|
|
DiffCheck diffcheck1(Curr1, (i == 0) ? Prev1 : (i == 1) ? Next0 : (i == 2) ? Curr2 : Next2, diff1);
|
|
|
|
wire [7:0] new_pattern = {diff1, diff0, pattern[7:2]};
|
|
|
|
wire [17:0] X = (i == 0) ? A : (i == 1) ? Prev1 : (i == 2) ? Next1 : G;
|
|
wire [17:0] blend_result;
|
|
Blend blender(hqTable[nextpatt], disable_hq2x, Curr0, X, B, D, F, H, blend_result);
|
|
|
|
reg Curr2_addr1;
|
|
reg [AWIDTH:0] Curr2_addr2;
|
|
wire [17:0] Curr2 = HALF_DEPTH ? h2rgb(Curr2tmp) : Curr2tmp;
|
|
wire [DWIDTH:0] Curr2tmp;
|
|
|
|
reg [AWIDTH:0] wrin_addr2;
|
|
reg [DWIDTH:0] wrpix;
|
|
reg wrin_en;
|
|
|
|
function [17:0] h2rgb;
|
|
input [8:0] v;
|
|
begin
|
|
h2rgb = mono ? {v[5:3],v[2:0], v[5:3],v[2:0], v[5:3],v[2:0]} : {v[8:6],v[8:6],v[5:3],v[5:3],v[2:0],v[2:0]};
|
|
end
|
|
endfunction
|
|
|
|
function [8:0] rgb2h;
|
|
input [17:0] v;
|
|
begin
|
|
rgb2h = mono ? {3'b000, v[17:15], v[14:12]} : {v[17:15], v[11:9], v[5:3]};
|
|
end
|
|
endfunction
|
|
|
|
hq2x_in #(.LENGTH(LENGTH), .DWIDTH(DWIDTH)) hq2x_in
|
|
(
|
|
.clk(clk),
|
|
|
|
.rdaddr(Curr2_addr2),
|
|
.rdbuf(Curr2_addr1),
|
|
.q(Curr2tmp),
|
|
|
|
.wraddr(wrin_addr2),
|
|
.wrbuf(iobuf),
|
|
.data(wrpix),
|
|
.wren(wrin_en)
|
|
);
|
|
|
|
reg [1:0] wrout_addr1;
|
|
reg [AWIDTH+1:0] wrout_addr2;
|
|
reg wrout_en;
|
|
reg [DWIDTH:0] wrdata;
|
|
|
|
hq2x_out #(.LENGTH(LENGTH), .DWIDTH(DWIDTH)) hq2x_out
|
|
(
|
|
.clk(clk),
|
|
|
|
.rdaddr(read_x),
|
|
.rdbuf(read_y),
|
|
.q(outpixel),
|
|
|
|
.wraddr(wrout_addr2),
|
|
.wrbuf(wrout_addr1),
|
|
.data(wrdata),
|
|
.wren(wrout_en)
|
|
);
|
|
|
|
always @(posedge clk) begin
|
|
reg [AWIDTH:0] offs;
|
|
reg old_reset_line;
|
|
reg old_reset_frame;
|
|
|
|
wrout_en <= 0;
|
|
wrin_en <= 0;
|
|
|
|
if(ce_x4) begin
|
|
|
|
pattern <= new_pattern;
|
|
|
|
if(~&offs) begin
|
|
if (i == 0) begin
|
|
Curr2_addr1 <= prevbuf;
|
|
Curr2_addr2 <= offs;
|
|
end
|
|
if (i == 1) begin
|
|
Prev2 <= Curr2;
|
|
Curr2_addr1 <= curbuf;
|
|
Curr2_addr2 <= offs;
|
|
end
|
|
if (i == 2) begin
|
|
Next2 <= HALF_DEPTH ? h2rgb(inputpixel) : inputpixel;
|
|
wrpix <= inputpixel;
|
|
wrin_addr2 <= offs;
|
|
wrin_en <= 1;
|
|
end
|
|
if (i == 3) begin
|
|
offs <= offs + 1'd1;
|
|
end
|
|
|
|
if(HALF_DEPTH) wrdata <= rgb2h(blend_result);
|
|
else wrdata <= blend_result;
|
|
|
|
wrout_addr1 <= {curbuf, i[1]};
|
|
wrout_addr2 <= {offs, i[1]^i[0]};
|
|
wrout_en <= 1;
|
|
end
|
|
|
|
if(i==3) begin
|
|
nextpatt <= {new_pattern[7:6], new_pattern[3], new_pattern[5], new_pattern[2], new_pattern[4], new_pattern[1:0]};
|
|
{A, G} <= {Prev0, Next0};
|
|
{B, F, H, D} <= {Prev1, Curr2, Next1, Curr0};
|
|
{Prev0, Prev1} <= {Prev1, Prev2};
|
|
{Curr0, Curr1} <= {Curr1, Curr2};
|
|
{Next0, Next1} <= {Next1, Next2};
|
|
end else begin
|
|
nextpatt <= {nextpatt[5], nextpatt[3], nextpatt[0], nextpatt[6], nextpatt[1], nextpatt[7], nextpatt[4], nextpatt[2]};
|
|
{B, F, H, D} <= {F, H, D, B};
|
|
end
|
|
|
|
i <= i + 1'b1;
|
|
if(old_reset_line && ~reset_line) begin
|
|
old_reset_frame <= reset_frame;
|
|
offs <= 0;
|
|
i <= 0;
|
|
y <= y + 1'd1;
|
|
prevbuf <= curbuf;
|
|
if(old_reset_frame & ~reset_frame) begin
|
|
y <= 0;
|
|
prevbuf <= 0;
|
|
end
|
|
end
|
|
|
|
old_reset_line <= reset_line;
|
|
end
|
|
end
|
|
|
|
endmodule // Hq2x
|