diff options
| author | Alejandro Soto <alejandro@34project.org> | 2024-05-26 11:16:04 -0600 |
|---|---|---|
| committer | Alejandro Soto <alejandro@34project.org> | 2024-05-26 12:51:51 -0600 |
| commit | 2e12077e682a27a159122b5676301c8c433a58fe (patch) | |
| tree | 338ee5116fdda32f6d7c988232dfe20c52a643b6 | |
| parent | e11c49cb39fd7957d74a1431e28efc4049d16799 (diff) | |
rtl/vdc: initial commit of video display controller
Diffstat (limited to '')
| -rw-r--r-- | rtl/mod.mk | 2 | ||||
| -rw-r--r-- | rtl/vdc/mod.mk | 18 | ||||
| -rw-r--r-- | rtl/vdc/vdc_dac.sv | 28 | ||||
| -rw-r--r-- | rtl/vdc/vdc_if.rdl | 85 | ||||
| -rw-r--r-- | rtl/vdc/vdc_io.sv | 184 | ||||
| -rw-r--r-- | rtl/vdc/vdc_pkg.sv | 22 | ||||
| -rw-r--r-- | rtl/vdc/vdc_sync.sv | 100 | ||||
| -rw-r--r-- | rtl/vdc/vdc_top.sv | 101 |
8 files changed, 539 insertions, 1 deletions
@@ -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 |
