1package device 2 3import chisel3._ 4import chisel3.util._ 5import chipsalliance.rocketchip.config.Parameters 6import freechips.rocketchip.amba.axi4.{AXI4AdapterNode, AXI4IdentityNode, AXI4Parameters, AXI4SlaveNode, AXI4SlaveParameters, AXI4SlavePortParameters, AXI4Xbar} 7import freechips.rocketchip.diplomacy.{AddressSet, LazyModule, LazyModuleImp, RegionType} 8import utils._ 9 10trait HasVGAConst { 11 val ScreenW = 800 12 val ScreenH = 600 13 14 val HFrontPorch = 56 15 val HActive = HFrontPorch + 120 16 val HBackPorch = HActive + ScreenW 17 val HTotal = HBackPorch + 64 18 val VFrontPorch = 37 19 val VActive = VFrontPorch + 6 20 val VBackPorch = VActive + ScreenH 21 val VTotal = VBackPorch + 23 22} 23 24trait HasHDMIConst { 25 val ScreenW = 800 26 val ScreenH = 600 27 28 val HFrontPorch = 40 29 val HActive = HFrontPorch + 128 30 val HBackPorch = HActive + ScreenW 31 val HTotal = HBackPorch + 88 32 val VFrontPorch = 1 33 val VActive = VFrontPorch + 4 34 val VBackPorch = VActive + ScreenH 35 val VTotal = VBackPorch + 23 36} 37 38trait HasVGAParameter extends HasHDMIConst { 39 val FBWidth = ScreenW / 2 40 val FBHeight = ScreenH / 2 41 val FBPixels = FBWidth * FBHeight 42} 43 44class VGABundle extends Bundle { 45 val rgb = Output(UInt(24.W)) 46 val hsync = Output(Bool()) 47 val vsync = Output(Bool()) 48 val valid = Output(Bool()) 49} 50 51class VGACtrlBundle extends Bundle { 52 val sync = Output(Bool()) 53} 54 55class VGACtrl 56( 57 address: AddressSet 58)(implicit p: Parameters) 59 extends AXI4SlaveModule(address, _extra = new VGACtrlBundle, executable = false) with HasVGAParameter { 60 override lazy val module = new AXI4SlaveModuleImp[VGACtrlBundle](this) { 61 62 val fbSizeReg = Cat(FBWidth.U(16.W), FBHeight.U(16.W)) 63 val sync = in.aw.fire() 64 65 val mapping = Map( 66 RegMap(0x0, fbSizeReg, RegMap.Unwritable), 67 RegMap(0x4, sync, RegMap.Unwritable) 68 ) 69 70 RegMap.generate(mapping, raddr(3, 0), in.r.bits.data, 71 waddr(3, 0), in.w.fire(), in.w.bits.data, MaskExpand(in.w.bits.strb)) 72 73 io.extra.get.sync := sync 74 } 75} 76 77class FBHelper extends BlackBox with HasBlackBoxInline { 78 val io = IO(new Bundle { 79 val clk = Input(Clock()) 80 val valid = Input(Bool()) 81 val pixel = Input(UInt(32.W)) 82 val sync = Input(Bool()) 83 }) 84 85 setInline("FBHelper.v", 86 s""" 87 |import "DPI-C" function void put_pixel(input int pixel); 88 |import "DPI-C" function void vmem_sync(); 89 | 90 |module FBHelper ( 91 | input clk, 92 | input valid, 93 | input [31:0] pixel, 94 | input sync 95 |); 96 | 97 | always@(posedge clk) begin 98 | if (valid) put_pixel(pixel); 99 | if (sync) vmem_sync(); 100 | end 101 | 102 |endmodule 103 """.stripMargin) 104} 105 106class AXI4VGA 107( 108 sim: Boolean = false, 109 fbAddress: AddressSet, 110 ctrlAddress: AddressSet 111)(implicit p: Parameters) 112 extends LazyModule with HasVGAParameter { 113 114 115 private val fb = LazyModule(new AXI4RAM( 116 fbAddress, 117 memByte= FBPixels * 4, 118 sim, 119 executable = false 120 )) 121 private val ctrl = LazyModule(new VGACtrl(ctrlAddress)) 122 123 val node = AXI4IdentityNode() 124 125 fb.node := node 126 ctrl.node := node 127 128 lazy val module = new LazyModuleImp(this) { 129 130 val io = IO(new Bundle() { 131 val vga = new VGABundle 132 }) 133 134 val out_fb = node.out.head._1 135 val out_ctrl = node.out.last._1 136 val in_fb = node.in.head._1 137 val in_ctrl = node.in.last._1 138 139 in_fb.ar.ready := true.B 140 in_fb.r.bits.data := 0.U 141 in_fb.r.bits.resp := AXI4Parameters.RESP_OKAY 142 in_fb.r.valid := BoolStopWatch(in_fb.ar.fire(), in_fb.r.fire(), startHighPriority = true) 143 144 def inRange(x: UInt, start: Int, end: Int) = (x >= start.U) && (x < end.U) 145 146 val (hCounter, hFinish) = Counter(true.B, HTotal) 147 val (vCounter, vFinish) = Counter(hFinish, VTotal) 148 io.vga.hsync := hCounter >= HFrontPorch.U 149 io.vga.vsync := vCounter >= VFrontPorch.U 150 151 val hInRange = inRange(hCounter, HActive, HBackPorch) 152 val vInRange = inRange(vCounter, VActive, VBackPorch) 153 io.vga.valid := hInRange && vInRange 154 155 val hCounterIsOdd = hCounter(0) 156 val hCounterIs2 = hCounter(1, 0) === 2.U 157 val vCounterIsOdd = vCounter(0) 158 // there is 2 cycle latency to read block memory, 159 // so we should issue the read request 2 cycle eariler 160 val nextPixel = inRange(hCounter, HActive - 1, HBackPorch - 1) && vInRange && hCounterIsOdd 161 val fbPixelAddrV0 = Counter(nextPixel && !vCounterIsOdd, FBPixels)._1 162 val fbPixelAddrV1 = Counter(nextPixel && vCounterIsOdd, FBPixels)._1 163 164 // each pixel is 4 bytes 165 out_fb.ar.bits.prot := 0.U 166 out_fb.ar.bits.addr := Cat(Mux(vCounterIsOdd, fbPixelAddrV1, fbPixelAddrV0), 0.U(2.W)) 167 out_fb.ar.valid := RegNext(nextPixel) && hCounterIs2 168 169 out_fb.r.ready := true.B 170 val data = HoldUnless(out_fb.r.bits.data, out_fb.r.fire()) 171 val color = Mux(hCounter(1), data(63, 32), data(31, 0)) 172 io.vga.rgb := Mux(io.vga.valid, color(23, 0), 0.U) 173 174 if (sim) { 175 val fbHelper = Module(new FBHelper) 176 fbHelper.io.clk := clock 177 fbHelper.io.valid := io.vga.valid 178 fbHelper.io.pixel := color 179 fbHelper.io.sync := ctrl.module.io.extra.get.sync 180 } 181 182 } 183 184 // val AXIidBits = 2 185 // val io = IO(new Bundle { 186 // val in = new Bundle { 187 // val fb = Flipped(new AXI4Lite) 188 // val ctrl = Flipped(new AXI4Lite) 189 // } 190 // val vga = new VGABundle 191 // }) 192 // 193 // val ctrl = Module(new VGACtrl) 194 // io.in.ctrl <> ctrl.io.in 195 // val fb = Module(new AXI4RAM(new AXI4Lite, memByte = FBPixels * 4)) 196 // // writable by axi4lite 197 // // but it only readable by the internel controller 198 // fb.io.in.aw <> io.in.fb.aw 199 // fb.io.in.w <> io.in.fb.w 200 // io.in.fb.b <> fb.io.in.b 201 // io.in.fb.ar.ready := true.B 202 // io.in.fb.r.bits.data := 0.U 203 // io.in.fb.r.bits.resp := AXI4Parameters.RESP_OKAY 204 // io.in.fb.r.valid := BoolStopWatch(io.in.fb.ar.fire(), io.in.fb.r.fire(), startHighPriority = true) 205 // 206 // def inRange(x: UInt, start: Int, end: Int) = (x >= start.U) && (x < end.U) 207 // 208 // val (hCounter, hFinish) = Counter(true.B, HTotal) 209 // val (vCounter, vFinish) = Counter(hFinish, VTotal) 210 // 211 // io.vga.hsync := hCounter >= HFrontPorch.U 212 // io.vga.vsync := vCounter >= VFrontPorch.U 213 // 214 // val hInRange = inRange(hCounter, HActive, HBackPorch) 215 // val vInRange = inRange(vCounter, VActive, VBackPorch) 216 // io.vga.valid := hInRange && vInRange 217 // 218 // val hCounterIsOdd = hCounter(0) 219 // val hCounterIs2 = hCounter(1,0) === 2.U 220 // val vCounterIsOdd = vCounter(0) 221 // // there is 2 cycle latency to read block memory, 222 // // so we should issue the read request 2 cycle eariler 223 // val nextPixel = inRange(hCounter, HActive - 1, HBackPorch - 1) && vInRange && hCounterIsOdd 224 // val fbPixelAddrV0 = Counter(nextPixel && !vCounterIsOdd, FBPixels)._1 225 // val fbPixelAddrV1 = Counter(nextPixel && vCounterIsOdd, FBPixels)._1 226 // 227 // // each pixel is 4 bytes 228 // fb.io.in.ar.bits.prot := 0.U 229 // fb.io.in.ar.bits.addr := Cat(Mux(vCounterIsOdd, fbPixelAddrV1, fbPixelAddrV0), 0.U(2.W)) 230 // fb.io.in.ar.valid := RegNext(nextPixel) && hCounterIs2 231 // 232 // fb.io.in.r.ready := true.B 233 // val data = HoldUnless(fb.io.in.r.bits.data, fb.io.in.r.fire()) 234 // val color = Mux(hCounter(1), data(63, 32), data(31, 0)) 235 // io.vga.rgb := Mux(io.vga.valid, color(23, 0), 0.U) 236 // 237 // if (sim) { 238 // val fbHelper = Module(new FBHelper) 239 // fbHelper.io.clk := clock 240 // fbHelper.io.valid := io.vga.valid 241 // fbHelper.io.pixel := color 242 // fbHelper.io.sync := ctrl.io.extra.get.sync 243 // } 244} 245