summaryrefslogtreecommitdiff
path: root/rtl
diff options
context:
space:
mode:
Diffstat (limited to 'rtl')
-rw-r--r--rtl/mod.mk2
-rw-r--r--rtl/vdc/mod.mk18
-rw-r--r--rtl/vdc/vdc_dac.sv28
-rw-r--r--rtl/vdc/vdc_if.rdl85
-rw-r--r--rtl/vdc/vdc_io.sv184
-rw-r--r--rtl/vdc/vdc_pkg.sv22
-rw-r--r--rtl/vdc/vdc_sync.sv100
-rw-r--r--rtl/vdc/vdc_top.sv101
8 files changed, 539 insertions, 1 deletions
diff --git a/rtl/mod.mk b/rtl/mod.mk
index c7e259a..07bbe7b 100644
--- a/rtl/mod.mk
+++ b/rtl/mod.mk
@@ -1,5 +1,5 @@
cores := config debounce intc
-subdirs := axilemu cache core dma_axi32 if_common fpu gfx legacy_gfx perf picorv32 pkt_switch smp top wb2axip
+subdirs := axilemu cache core dma_axi32 if_common fpu gfx legacy_gfx perf picorv32 pkt_switch smp top vdc wb2axip
define core/config
$(this)/rtl_include_dirs := .
diff --git a/rtl/vdc/mod.mk b/rtl/vdc/mod.mk
new file mode 100644
index 0000000..11bf8c3
--- /dev/null
+++ b/rtl/vdc/mod.mk
@@ -0,0 +1,18 @@
+cores := vdc_if
+
+define core
+ $(this)/deps := if_common vdc_if
+
+ $(this)/rtl_top := vdc_top
+ $(this)/rtl_dirs := .
+ $(this)/rtl_files := vdc_pkg.sv vdc_top.sv
+endef
+
+define core/vdc_if
+ $(this)/hooks := regblock
+
+ $(this)/regblock_rdl := vdc_if.rdl
+ $(this)/regblock_top := vdc_if
+ $(this)/regblock_args := --default-reset arst_n
+ $(this)/regblock_cpuif := axi4-lite
+endef
diff --git a/rtl/vdc/vdc_dac.sv b/rtl/vdc/vdc_dac.sv
new file mode 100644
index 0000000..f7bb4b4
--- /dev/null
+++ b/rtl/vdc/vdc_dac.sv
@@ -0,0 +1,28 @@
+interface vdc_dac;
+
+ import vdc_pkg::*;
+
+ logic first, last, ready, valid;
+ pix_rgb30 pix;
+
+ modport tx
+ (
+ input ready,
+
+ output pix,
+ last,
+ first,
+ valid
+ );
+
+ modport rx
+ (
+ input pix,
+ last,
+ first,
+ valid,
+
+ output ready
+ );
+
+endinterface
diff --git a/rtl/vdc/vdc_if.rdl b/rtl/vdc/vdc_if.rdl
new file mode 100644
index 0000000..30c8dd3
--- /dev/null
+++ b/rtl/vdc/vdc_if.rdl
@@ -0,0 +1,85 @@
+addrmap vdc_if {
+ name = "Video display controller";
+
+ default hw = r;
+ default sw = rw;
+ default regwidth = 32;
+ default precedence = hw;
+
+ reg {
+ name = "Control and status register";
+
+ field {
+ desc = "Enable video DAC output";
+ } DACEN[0:0] = 0;
+
+ field {
+ desc = "Video DAC output is active";
+
+ hw = w;
+ sw = r;
+ } DACON[1:1] = 0;
+
+ field {
+ desc = "Enable double buffering";
+ } DOUBLEBUFF[2:2] = 0;
+ } CTRL @ 0x00;
+
+ reg {
+ name = "Output resolution geometry";
+
+ field {
+ desc = "Vertical lines minus one";
+ } LINES[15:0];
+
+ field {
+ desc = "Line length in words minus one";
+ } LENGTH[31:16];
+ } GEOMETRY @ 0x04;
+
+ reg {
+ name = "Buffer stream access and format";
+
+ field {
+ desc = "Horizontal stride";
+ } HSTRIDE[15:0];
+ } STREAM @ 0x08;
+
+ reg {
+ name = "Base address of the primary buffer";
+
+ field {
+ desc = "Base address in words";
+
+ hw = rw;
+ precedence = sw;
+
+ we;
+ } ADDR[31:2];
+ } FRONT @ 0x0c;
+
+ reg {
+ name = "Back buffer";
+
+ field {
+ desc = "Base address in words";
+
+ hw = rw;
+ precedence = sw;
+
+ we;
+ swmod;
+ } ADDR[31:2];
+ } BACK @ 0x10;
+
+ reg {
+ name = "Retired back buffer";
+
+ default hw = w;
+ default sw = r;
+
+ field {
+ desc = "Base address in words";
+ } ADDR[31:2];
+ } RETIRE @ 0x14;
+};
diff --git a/rtl/vdc/vdc_io.sv b/rtl/vdc/vdc_io.sv
new file mode 100644
index 0000000..88b19e3
--- /dev/null
+++ b/rtl/vdc/vdc_io.sv
@@ -0,0 +1,184 @@
+module vdc_io
+import vdc_pkg::*;
+(
+ input logic clk,
+ rst_n,
+
+ input logic frame_start,
+ input ptr front_base,
+ input geom_dim lines,
+ stride,
+ line_len,
+ output logic frame_done,
+
+ if_axib.m stream,
+
+ vdc_dac.tx dac
+);
+
+ localparam int BURST_BITS = 4;
+
+ enum int unsigned
+ {
+ ORDER_B_RGB,
+ ORDER_GB_RG,
+ ORDER_RGB_R,
+ ORDER_BUBBLE
+ } word_order;
+
+ ptr araddr, stride_jump, stride_save;
+ logic bubble, burst_ar_last, first_pix, last_pix, next_bubble, word_r_last;
+ geom_dim line_ar_count, line_r_count, word_r_count;
+ pix_rgb24 dac_pix, next_pix;
+ logic[3:0][7:0] in_cur, in_prev;
+ logic[$bits(geom_dim) - BURST_BITS - 1:0] burst_ar_count, bursts_per_line;
+
+ assign stream.arid = '0;
+ assign stream.arlen = ($bits(stream.arlen))'((1 << BURST_BITS) - 1);
+ assign stream.arsize = 3'b010; // 4 bytes/beat
+ assign stream.araddr = {araddr, 2'b00};
+ assign stream.arburst = 2'b01; // Incremental mode
+
+ assign stream.rready = (~dac.valid | dac.ready) & ~bubble;
+
+ assign stream.awid = 'x;
+ assign stream.awlen = 'x;
+ assign stream.awsize = 'x;
+ assign stream.awaddr = 'x;
+ assign stream.awburst = 'x;
+ assign stream.awvalid = 0;
+
+ assign stream.wdata = 'x;
+ assign stream.wlast = 'x;
+ assign stream.wstrb = 'x;
+ assign stream.wvalid = 0;
+
+ assign stream.bready = 0;
+
+ assign dac.pix.b = rgb8to10(dac_pix.b);
+ assign dac.pix.g = rgb8to10(dac_pix.g);
+ assign dac.pix.r = rgb8to10(dac_pix.r);
+
+ assign in_cur = stream.rdata;
+ assign last_pix = bubble & word_r_last & (line_r_count == '0);
+ assign stride_jump = stride_save + {{($bits(araddr) - $bits(stride)){1'b0}}, stride};
+ assign word_r_last = word_r_count == '0;
+ assign burst_ar_last = burst_ar_count == '0;
+ assign bursts_per_line = line_len[$bits(line_len) - 1:BURST_BITS];
+
+ always_comb begin
+ unique case (word_order)
+ ORDER_RGB_R: next_bubble = 1;
+ default: next_bubble = 0;
+ endcase
+
+ unique case (word_order)
+ ORDER_BUBBLE: bubble = 1;
+ default: bubble = 0;
+ endcase
+
+ unique case (word_order)
+ ORDER_B_RGB: begin
+ next_pix.b = in_cur[0];
+ next_pix.g = in_cur[1];
+ next_pix.r = in_cur[2];
+ end
+
+ ORDER_GB_RG: begin
+ next_pix.b = in_prev[3];
+ next_pix.g = in_cur[0];
+ next_pix.r = in_cur[1];
+ end
+
+ ORDER_RGB_R: begin
+ next_pix.b = in_prev[2];
+ next_pix.g = in_prev[3];
+ next_pix.r = in_cur[0];
+ end
+
+ ORDER_BUBBLE: begin
+ next_pix.b = in_prev[1];
+ next_pix.g = in_prev[2];
+ next_pix.r = in_prev[3];
+ end
+ endcase
+ end
+
+ always_ff @(posedge clk) begin
+ if (stream.arvalid & stream.arready) begin
+ araddr <= araddr + ($bits(araddr))'(1 << BURST_BITS);
+
+ burst_ar_count <= burst_ar_count - 1;
+ if (burst_ar_last) begin
+ araddr <= stride_jump;
+ stride_save <= stride_jump;
+
+ burst_ar_count <= bursts_per_line;
+ line_ar_count <= line_ar_count - 1;
+ end
+ end
+
+ if (stream.rvalid & stream.rready)
+ in_prev <= in_cur;
+
+ if (~dac.valid | dac.ready) begin
+ dac_pix <= next_pix;
+ dac.last <= last_pix;
+ dac.first <= first_pix;
+ end
+
+ if (dac.valid & dac.ready) begin
+ first_pix <= 0;
+
+ unique case (word_order)
+ ORDER_B_RGB: word_order <= ORDER_GB_RG;
+ ORDER_GB_RG: word_order <= ORDER_RGB_R;
+ ORDER_RGB_R: word_order <= ORDER_BUBBLE;
+ ORDER_BUBBLE: word_order <= ORDER_B_RGB;
+ endcase
+
+ if (~next_bubble) begin
+ word_r_count <= word_r_count - 1;
+ if (word_r_last) begin
+ line_r_count <= line_r_count - 1;
+ word_r_count <= line_len;
+ end
+ end
+ end
+
+ if (frame_start) begin
+ araddr <= front_base;
+ stride_save <= front_base;
+ line_ar_count <= lines;
+ burst_ar_count <= bursts_per_line;
+
+ first_pix <= 1;
+ word_order <= ORDER_B_RGB;
+
+ line_r_count <= lines;
+ word_r_count <= line_len;
+ end
+ end
+
+ always_ff @(posedge clk or negedge rst_n)
+ if (~rst_n) begin
+ dac.valid <= 0;
+ frame_done <= 0;
+ stream.arvalid <= 0;
+ end else begin
+ dac.valid <= (dac.valid & ~dac.ready) | bubble | stream.rvalid;
+
+ unique case (1'b1)
+ frame_start:
+ stream.arvalid <= 1;
+
+ stream.arvalid & stream.arready & burst_ar_last & (line_ar_count == '0):
+ stream.arvalid <= 0;
+
+ default: ;
+ endcase
+
+ frame_done <= dac.valid & dac.ready & last_pix;
+ end
+
+endmodule
diff --git a/rtl/vdc/vdc_pkg.sv b/rtl/vdc/vdc_pkg.sv
new file mode 100644
index 0000000..3035f94
--- /dev/null
+++ b/rtl/vdc/vdc_pkg.sv
@@ -0,0 +1,22 @@
+package vdc_pkg;
+
+ typedef logic[7:0] rgb8;
+ typedef logic[9:0] rgb10;
+ typedef logic[15:0] geom_dim;
+ typedef logic[29:0] ptr;
+
+ typedef struct
+ {
+ rgb8 r, g, b;
+ } pix_rgb24;
+
+ typedef struct
+ {
+ rgb10 r, g, b;
+ } pix_rgb30;
+
+ function rgb10 rgb8to10(rgb8 in);
+ return {in, in[0], in[0]};
+ endfunction
+
+endpackage
diff --git a/rtl/vdc/vdc_sync.sv b/rtl/vdc/vdc_sync.sv
new file mode 100644
index 0000000..11cb441
--- /dev/null
+++ b/rtl/vdc/vdc_sync.sv
@@ -0,0 +1,100 @@
+module vdc_sync
+import vdc_pkg::*;
+(
+ input logic clk,
+ rst_n,
+
+ input logic csr_back_push,
+ csr_dac_enable,
+ csr_double_buff,
+ input ptr csr_back,
+ csr_front,
+ input geom_dim csr_lines,
+ csr_stride,
+ csr_line_len,
+
+ output logic csr_dac_on,
+ csr_back_set,
+ csr_front_set,
+ output ptr csr_retired,
+ csr_back_next,
+ csr_front_next,
+
+ input logic frame_done,
+
+ output logic frame_start,
+ output ptr front_base,
+ output geom_dim lines,
+ stride,
+ line_len
+);
+
+ enum int unsigned
+ {
+ OFF,
+ LOCK,
+ START,
+ RUN
+ } next_state, state;
+
+ logic lock;
+
+ assign csr_back_set = lock & csr_double_buff & ~csr_back_push;
+ assign csr_back_next = csr_front;
+ assign csr_front_set = csr_back_set;
+ assign csr_front_next = csr_back;
+
+ always_comb begin
+ next_state = state;
+ unique case (state)
+ OFF:
+ if (csr_dac_enable)
+ next_state = LOCK;
+
+ LOCK:
+ if (~csr_back_push)
+ next_state = csr_dac_enable ? START : OFF;
+
+ START:
+ next_state = RUN;
+
+ RUN:
+ if (frame_done)
+ next_state = LOCK;
+ endcase
+
+ unique case (state)
+ OFF: csr_dac_on = 0;
+ default: csr_dac_on = 1;
+ endcase
+
+ unique case (state)
+ LOCK: lock = 1;
+ default: lock = 0;
+ endcase
+
+ unique case (state)
+ START: frame_start = 1;
+ default: frame_start = 0;
+ endcase
+ end
+
+ always_ff @(posedge clk or negedge rst_n)
+ if (~rst_n)
+ state <= OFF;
+ else
+ state <= next_state;
+
+ always_ff @(posedge clk) begin
+ if (csr_back_push)
+ csr_retired <= csr_back;
+
+ if (lock) begin
+ lines <= csr_lines;
+ stride <= csr_stride;
+ line_len <= csr_line_len;
+ front_base <= csr_double_buff ? csr_back : csr_front;
+ end
+ end
+
+endmodule
diff --git a/rtl/vdc/vdc_top.sv b/rtl/vdc/vdc_top.sv
new file mode 100644
index 0000000..f7dacdd
--- /dev/null
+++ b/rtl/vdc/vdc_top.sv
@@ -0,0 +1,101 @@
+module vdc_top
+import vdc_pkg::*, vdc_if_pkg::*;
+(
+ input logic clk,
+ rst_n,
+
+ if_axil.s host,
+
+ if_axib.m stream,
+
+ vdc_dac.tx dac
+);
+
+ axi4lite_intf #(.ADDR_WIDTH(VDC_IF_MIN_ADDR_WIDTH)) regblock();
+ vdc_if__in_t if_in;
+ vdc_if__out_t if_out;
+
+ logic csr_back_push, csr_dac_enable, csr_dac_on, csr_double_buff,
+ csr_back_set, csr_front_set, frame_done, frame_start;
+
+ ptr csr_back, csr_back_next, csr_front, csr_front_next, csr_retired, front_base;
+ geom_dim csr_lines, csr_line_len, csr_stride, lines, line_len, stride;
+
+ assign csr_dac_enable = if_out.CTRL.DACEN.value;
+ assign csr_double_buff = if_out.CTRL.DOUBLEBUFF.value;
+ assign if_in.CTRL.DACON.next = csr_dac_on;
+
+ assign csr_lines = if_out.GEOMETRY.LINES.value;
+ assign csr_line_len = if_out.GEOMETRY.LENGTH.value;
+
+ assign csr_stride = if_out.STREAM.HSTRIDE.value;
+
+ assign csr_front = if_out.FRONT.ADDR.value;
+ assign if_in.FRONT.ADDR.we = csr_front_set;
+ assign if_in.FRONT.ADDR.next = csr_front_next;
+
+ assign csr_back = if_out.BACK.ADDR.value;
+ assign csr_back_push = if_out.BACK.ADDR.swmod;
+ assign if_in.BACK.ADDR.we = csr_back_set;
+ assign if_in.BACK.ADDR.next = csr_back_next;
+
+ assign if_in.RETIRE.ADDR.next = csr_retired;
+
+ vdc_if regif
+ (
+ .clk,
+ .arst_n(rst_n),
+ .s_axil(regblock.slave),
+ .hwif_in(if_in),
+ .hwif_out(if_out)
+ );
+
+ if_axil2regblock axil2regblock
+ (
+ .axis(host),
+ .axim(regblock.master)
+ );
+
+ vdc_io io
+ (
+ .clk,
+ .rst_n,
+ .dac,
+ .lines,
+ .stream,
+ .stride,
+ .line_len,
+ .frame_done,
+ .front_base,
+ .frame_start
+ );
+
+ vdc_sync sync
+ (
+ .clk,
+ .rst_n,
+
+ .csr_back,
+ .csr_front,
+ .csr_lines,
+ .csr_dac_on,
+ .csr_stride,
+ .csr_retired,
+ .csr_back_set,
+ .csr_line_len,
+ .csr_back_next,
+ .csr_back_push,
+ .csr_front_set,
+ .csr_dac_enable,
+ .csr_front_next,
+ .csr_double_buff,
+
+ .lines,
+ .stride,
+ .line_len,
+ .frame_done,
+ .front_base,
+ .frame_start
+ );
+
+endmodule