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