1package device 2 3import chisel3._ 4import chisel3.util._ 5 6import bus.axi4._ 7import utils._ 8 9trait HasVGAConst { 10 val ScreenW = 800 11 val ScreenH = 600 12 13 val HFrontPorch = 56 14 val HActive = HFrontPorch + 120 15 val HBackPorch = HActive + ScreenW 16 val HTotal = HBackPorch + 64 17 val VFrontPorch = 37 18 val VActive = VFrontPorch + 6 19 val VBackPorch = VActive + ScreenH 20 val VTotal = VBackPorch + 23 21} 22 23trait HasHDMIConst { 24 val ScreenW = 800 25 val ScreenH = 600 26 27 val HFrontPorch = 40 28 val HActive = HFrontPorch + 128 29 val HBackPorch = HActive + ScreenW 30 val HTotal = HBackPorch + 88 31 val VFrontPorch = 1 32 val VActive = VFrontPorch + 4 33 val VBackPorch = VActive + ScreenH 34 val VTotal = VBackPorch + 23 35} 36 37trait HasVGAParameter extends HasHDMIConst { 38 val FBWidth = ScreenW / 2 39 val FBHeight = ScreenH / 2 40 val FBPixels = FBWidth * FBHeight 41} 42 43class VGABundle extends Bundle { 44 val rgb = Output(UInt(24.W)) 45 val hsync = Output(Bool()) 46 val vsync = Output(Bool()) 47 val valid = Output(Bool()) 48} 49 50class VGACtrlBundle extends Bundle { 51 val sync = Output(Bool()) 52} 53 54class VGACtrl extends AXI4SlaveModule(new AXI4Lite, new VGACtrlBundle) with HasVGAParameter { 55 val fbSizeReg = Cat(FBWidth.U(16.W), FBHeight.U(16.W)) 56 val sync = in.aw.fire() 57 58 val mapping = Map( 59 RegMap(0x0, fbSizeReg, RegMap.Unwritable), 60 RegMap(0x4, sync, RegMap.Unwritable) 61 ) 62 63 RegMap.generate(mapping, raddr(3,0), in.r.bits.data, 64 waddr(3,0), in.w.fire(), in.w.bits.data, MaskExpand(in.w.bits.strb)) 65 66 io.extra.get.sync := sync 67} 68 69class FBHelper extends BlackBox with HasBlackBoxInline { 70 val io = IO(new Bundle { 71 val clk = Input(Clock()) 72 val valid = Input(Bool()) 73 val pixel = Input(UInt(32.W)) 74 val sync = Input(Bool()) 75 }) 76 77 setInline("FBHelper.v", 78 s""" 79 |import "DPI-C" function void put_pixel(input int pixel); 80 |import "DPI-C" function void vmem_sync(); 81 | 82 |module FBHelper ( 83 | input clk, 84 | input valid, 85 | input [31:0] pixel, 86 | input sync 87 |); 88 | 89 | always@(posedge clk) begin 90 | if (valid) put_pixel(pixel); 91 | if (sync) vmem_sync(); 92 | end 93 | 94 |endmodule 95 """.stripMargin) 96} 97 98class AXI4VGA(sim: Boolean = false) extends Module with HasVGAParameter { 99 val AXIidBits = 2 100 val io = IO(new Bundle { 101 val in = new Bundle { 102 val fb = Flipped(new AXI4Lite) 103 val ctrl = Flipped(new AXI4Lite) 104 } 105 val vga = new VGABundle 106 }) 107 108 val ctrl = Module(new VGACtrl) 109 io.in.ctrl <> ctrl.io.in 110 val fb = Module(new AXI4RAM(new AXI4Lite, memByte = FBPixels * 4)) 111 // writable by axi4lite 112 // but it only readable by the internel controller 113 fb.io.in.aw <> io.in.fb.aw 114 fb.io.in.w <> io.in.fb.w 115 io.in.fb.b <> fb.io.in.b 116 io.in.fb.ar.ready := true.B 117 io.in.fb.r.bits.data := 0.U 118 io.in.fb.r.bits.resp := AXI4Parameters.RESP_OKAY 119 io.in.fb.r.valid := BoolStopWatch(io.in.fb.ar.fire(), io.in.fb.r.fire(), startHighPriority = true) 120 121 def inRange(x: UInt, start: Int, end: Int) = (x >= start.U) && (x < end.U) 122 123 val (hCounter, hFinish) = Counter(true.B, HTotal) 124 val (vCounter, vFinish) = Counter(hFinish, VTotal) 125 126 io.vga.hsync := hCounter >= HFrontPorch.U 127 io.vga.vsync := vCounter >= VFrontPorch.U 128 129 val hInRange = inRange(hCounter, HActive, HBackPorch) 130 val vInRange = inRange(vCounter, VActive, VBackPorch) 131 io.vga.valid := hInRange && vInRange 132 133 val hCounterIsOdd = hCounter(0) 134 val hCounterIs2 = hCounter(1,0) === 2.U 135 val vCounterIsOdd = vCounter(0) 136 // there is 2 cycle latency to read block memory, 137 // so we should issue the read request 2 cycle eariler 138 val nextPixel = inRange(hCounter, HActive - 1, HBackPorch - 1) && vInRange && hCounterIsOdd 139 val fbPixelAddrV0 = Counter(nextPixel && !vCounterIsOdd, FBPixels)._1 140 val fbPixelAddrV1 = Counter(nextPixel && vCounterIsOdd, FBPixels)._1 141 142 // each pixel is 4 bytes 143 fb.io.in.ar.bits.prot := 0.U 144 fb.io.in.ar.bits.addr := Cat(Mux(vCounterIsOdd, fbPixelAddrV1, fbPixelAddrV0), 0.U(2.W)) 145 fb.io.in.ar.valid := RegNext(nextPixel) && hCounterIs2 146 147 fb.io.in.r.ready := true.B 148 val data = HoldUnless(fb.io.in.r.bits.data, fb.io.in.r.fire()) 149 val color = Mux(hCounter(1), data(63, 32), data(31, 0)) 150 io.vga.rgb := Mux(io.vga.valid, color(23, 0), 0.U) 151 152 if (sim) { 153 val fbHelper = Module(new FBHelper) 154 fbHelper.io.clk := clock 155 fbHelper.io.valid := io.vga.valid 156 fbHelper.io.pixel := color 157 fbHelper.io.sync := ctrl.io.extra.get.sync 158 } 159} 160