1package device 2 3import chisel3._ 4import chisel3.util._ 5 6import bus.axi4._ 7import utils._ 8 9trait HasVGAConst { 10 // these are only fit for 800x600 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 val FBWidth = ScreenW / 2 24 val FBHeight = ScreenH / 2 25 val FBPixels = FBWidth * FBHeight 26} 27 28class VGABundle extends Bundle { 29 val r = Output(UInt(4.W)) 30 val g = Output(UInt(4.W)) 31 val b = Output(UInt(4.W)) 32 val hsync = Output(Bool()) 33 val vsync = Output(Bool()) 34} 35 36class VGACtrl extends AXI4SlaveModule(new AXI4Lite) with HasVGAConst { 37 // actually this is a constant 38 val fbSizeReg = Cat(FBWidth.U(16.W), FBHeight.U(16.W)) 39 // we always return fbSizeReg to axi4lite 40 in.r.bits.data := fbSizeReg 41 val sync = in.aw.fire() 42} 43 44class AXI4VGA extends Module with HasVGAConst { 45 // need a 50MHz clock 46 val io = IO(new Bundle { 47 val in = new Bundle { 48 val fb = Flipped(new AXI4Lite) 49 val ctrl = Flipped(new AXI4Lite) 50 } 51 val vga = new VGABundle 52 }) 53 54 val ctrl = Module(new VGACtrl) 55 io.in.ctrl <> ctrl.io.in 56 val fb = Module(new AXI4RAM(_type = new AXI4Lite, FBPixels * 4)) 57 // writable by axi4lite 58 // but it only readable by the internel controller 59 fb.io.in.aw <> io.in.fb.aw 60 fb.io.in.w <> io.in.fb.w 61 io.in.fb.b <> fb.io.in.b 62 io.in.fb.ar.ready := true.B 63 io.in.fb.r.bits.data := 0.U 64 io.in.fb.r.bits.resp := AXI4Parameters.RESP_OKAY 65 io.in.fb.r.valid := BoolStopWatch(io.in.fb.ar.fire(), io.in.fb.r.fire(), startHighPriority = true) 66 67 def inRange(x: UInt, start: Int, end: Int) = (x >= start.U) && (x < end.U) 68 69 val (hCounter, hFinish) = Counter(true.B, HTotal) 70 val (vCounter, vFinish) = Counter(hFinish, VTotal) 71 72 io.vga.hsync := hCounter >= HFrontPorch.U 73 io.vga.vsync := vCounter >= VFrontPorch.U 74 75 val hInRange = inRange(hCounter, HActive, HBackPorch) 76 val vInRange = inRange(vCounter, VActive, VBackPorch) 77 val videoValid = hInRange && vInRange 78 79 val hCounterIsOdd = hCounter(0) 80 val vCounterIsOdd = vCounter(0) 81 // there is 1 cycle latency to read block memory, 82 // so we should issue the read request 1 cycle eariler 83 val nextPixel = inRange(hCounter, HActive - 1, HBackPorch - 1) && vInRange && hCounterIsOdd 84 val fbPixelAddrV0 = Counter(nextPixel && !vCounterIsOdd, FBPixels)._1 85 val fbPixelAddrV1 = Counter(nextPixel && vCounterIsOdd, FBPixels)._1 86 87 // each pixel is 4 bytes 88 fb.io.in.ar.bits.addr := Cat(Mux(vCounterIsOdd, fbPixelAddrV1, fbPixelAddrV0), 0.U(2.W)) 89 fb.io.in.ar.bits.prot := DontCare 90 fb.io.in.ar.valid := nextPixel 91 92 fb.io.in.r.ready := true.B 93 val color = fb.io.in.r.bits.data 94 io.vga.r := Mux(videoValid, color(23, 20), 0.U) 95 io.vga.g := Mux(videoValid, color(15, 12), 0.U) 96 io.vga.b := Mux(videoValid, color(7, 4), 0.U) 97} 98