├── .github └── workflows │ └── test.yml ├── .gitignore ├── README.md ├── assets └── ff720p.jpg ├── build.sbt ├── build.sc ├── generate_all_verilog_sources.sh ├── platforms ├── tangnano4k │ ├── tangnano4k.cst │ └── tangnano4k.sdc ├── tangnano9k │ ├── TMDS_PLLVR.v │ ├── gowin_rpll.v │ ├── tangnano9k.cst │ └── tangnano9k.sdc └── ulx3s │ └── ulx3s_v20.lpf ├── project ├── build.properties └── plugins.sbt └── src ├── main └── scala │ ├── HdmiTx.scala │ ├── PatternExample.scala │ ├── Rgb2Tmds.scala │ ├── TmdsEncoder.scala │ ├── TmdsIncludes.scala │ ├── platforms │ ├── tangnano4k.scala │ ├── tangnano9k.scala │ └── ulx3s.scala │ └── video │ ├── VideoConsts.scala │ └── hvsync.scala └── test ├── python ├── PatternExample.py ├── austrianflag.png ├── belgianflag.png ├── danishflag.png ├── dutchflag.png ├── finnishflag.png ├── frenchflag.png ├── germanflag.png ├── greekflag.png ├── hgradient.png ├── hstripes.png ├── irishflag.png ├── italianflag.png ├── luxembourgishflag.png ├── norwegianflag.png ├── rainbow.png ├── scala2py.sh ├── spanishflag.png ├── swedishflag.png ├── ukraineflag.png ├── vgradient.png └── vstripes.png └── scala └── TmdsEncoder.scala /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | ci: 7 | name: ci 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v2 12 | - name: Cleanup 13 | run: sed -i "s/%NAME%/test/g" build.sc 14 | - name: Setup Scala 15 | uses: olafurpg/setup-scala@v10 16 | with: 17 | java-version: adopt@1.8 18 | - name: Setup Mill 19 | uses: jodersky/setup-mill@v0.2.3 20 | with: 21 | mill-version: 0.9.7 22 | - name: Cache Scala 23 | uses: coursier/cache-action@v5 24 | - name: SBT Test 25 | run: sbt test 26 | - name: mill Test 27 | run: mill _.test 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Project Specific stuff 2 | test_run_dir/* 3 | ### XilinxISE template 4 | # intermediate build files 5 | *.bgn 6 | *.bit 7 | *.bld 8 | *.cmd_log 9 | *.drc 10 | *.ll 11 | *.lso 12 | *.msd 13 | *.msk 14 | *.ncd 15 | *.ngc 16 | *.ngd 17 | *.ngr 18 | *.pad 19 | *.par 20 | *.pcf 21 | *.prj 22 | *.ptwx 23 | *.rbb 24 | *.rbd 25 | *.stx 26 | *.syr 27 | *.twr 28 | *.twx 29 | *.unroutes 30 | *.ut 31 | *.xpi 32 | *.xst 33 | *_bitgen.xwbt 34 | *_envsettings.html 35 | *_map.map 36 | *_map.mrp 37 | *_map.ngm 38 | *_map.xrpt 39 | *_ngdbuild.xrpt 40 | *_pad.csv 41 | *_pad.txt 42 | *_par.xrpt 43 | *_summary.html 44 | *_summary.xml 45 | *_usage.xml 46 | *_xst.xrpt 47 | 48 | # project-wide generated files 49 | *.gise 50 | par_usage_statistics.html 51 | usage_statistics_webtalk.html 52 | webtalk.log 53 | webtalk_pn.xml 54 | 55 | # generated folders 56 | iseconfig/ 57 | xlnx_auto_0_xdb/ 58 | xst/ 59 | _ngo/ 60 | _xmsgs/ 61 | ### Eclipse template 62 | *.pydevproject 63 | .metadata 64 | .gradle 65 | bin/ 66 | tmp/ 67 | *.tmp 68 | *.bak 69 | *.swp 70 | *~.nib 71 | local.properties 72 | .settings/ 73 | .loadpath 74 | 75 | # Eclipse Core 76 | .project 77 | 78 | # External tool builders 79 | .externalToolBuilders/ 80 | 81 | # Locally stored "Eclipse launch configurations" 82 | *.launch 83 | 84 | # CDT-specific 85 | .cproject 86 | 87 | # JDT-specific (Eclipse Java Development Tools) 88 | .classpath 89 | 90 | # Java annotation processor (APT) 91 | .factorypath 92 | 93 | # PDT-specific 94 | .buildpath 95 | 96 | # sbteclipse plugin 97 | .target 98 | 99 | # TeXlipse plugin 100 | .texlipse 101 | ### C template 102 | # Object files 103 | *.o 104 | *.ko 105 | *.obj 106 | *.elf 107 | 108 | # Precompiled Headers 109 | *.gch 110 | *.pch 111 | 112 | # Libraries 113 | *.lib 114 | *.a 115 | *.la 116 | *.lo 117 | 118 | # Shared objects (inc. Windows DLLs) 119 | *.dll 120 | *.so 121 | *.so.* 122 | *.dylib 123 | 124 | # Executables 125 | *.exe 126 | *.out 127 | *.app 128 | *.i*86 129 | *.x86_64 130 | *.hex 131 | 132 | # Debug files 133 | *.dSYM/ 134 | ### SBT template 135 | # Simple Build Tool 136 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control 137 | 138 | target/ 139 | lib_managed/ 140 | src_managed/ 141 | project/boot/ 142 | .history 143 | .cache 144 | ### Emacs template 145 | # -*- mode: gitignore; -*- 146 | *~ 147 | \#*\# 148 | /.emacs.desktop 149 | /.emacs.desktop.lock 150 | *.elc 151 | auto-save-list 152 | tramp 153 | .\#* 154 | 155 | # Org-mode 156 | .org-id-locations 157 | *_archive 158 | 159 | # flymake-mode 160 | *_flymake.* 161 | 162 | # eshell files 163 | /eshell/history 164 | /eshell/lastdir 165 | 166 | # elpa packages 167 | /elpa/ 168 | 169 | # reftex files 170 | *.rel 171 | 172 | # AUCTeX auto folder 173 | /auto/ 174 | 175 | # cask packages 176 | .cask/ 177 | ### Vim template 178 | [._]*.s[a-w][a-z] 179 | [._]s[a-w][a-z] 180 | *.un~ 181 | Session.vim 182 | .netrwhist 183 | *~ 184 | ### JetBrains template 185 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 186 | 187 | *.iml 188 | 189 | ## Directory-based project format: 190 | .idea/ 191 | # if you remove the above rule, at least ignore the following: 192 | 193 | # User-specific stuff: 194 | # .idea/workspace.xml 195 | # .idea/tasks.xml 196 | # .idea/dictionaries 197 | 198 | # Sensitive or high-churn files: 199 | # .idea/dataSources.ids 200 | # .idea/dataSources.xml 201 | # .idea/sqlDataSources.xml 202 | # .idea/dynamic.xml 203 | # .idea/uiDesigner.xml 204 | 205 | # Gradle: 206 | # .idea/gradle.xml 207 | # .idea/libraries 208 | 209 | # Mongo Explorer plugin: 210 | # .idea/mongoSettings.xml 211 | 212 | ## File-based project format: 213 | *.ipr 214 | *.iws 215 | 216 | ## Plugin-specific files: 217 | 218 | # IntelliJ 219 | /out/ 220 | 221 | # mpeltonen/sbt-idea plugin 222 | .idea_modules/ 223 | 224 | # JIRA plugin 225 | atlassian-ide-plugin.xml 226 | 227 | # Crashlytics plugin (for Android Studio and IntelliJ) 228 | com_crashlytics_export_strings.xml 229 | crashlytics.properties 230 | crashlytics-build.properties 231 | ### C++ template 232 | # Compiled Object files 233 | *.slo 234 | *.lo 235 | *.o 236 | *.obj 237 | 238 | # Precompiled Headers 239 | *.gch 240 | *.pch 241 | 242 | # Compiled Dynamic libraries 243 | *.so 244 | *.dylib 245 | *.dll 246 | 247 | # Fortran module files 248 | *.mod 249 | 250 | # Compiled Static libraries 251 | *.lai 252 | *.la 253 | *.a 254 | *.lib 255 | 256 | # Executables 257 | *.exe 258 | *.out 259 | *.app 260 | ### OSX template 261 | .DS_Store 262 | .AppleDouble 263 | .LSOverride 264 | 265 | # Icon must end with two \r 266 | Icon 267 | 268 | # Thumbnails 269 | ._* 270 | 271 | # Files that might appear in the root of a volume 272 | .DocumentRevisions-V100 273 | .fseventsd 274 | .Spotlight-V100 275 | .TemporaryItems 276 | .Trashes 277 | .VolumeIcon.icns 278 | 279 | # Directories potentially created on remote AFP share 280 | .AppleDB 281 | .AppleDesktop 282 | Network Trash Folder 283 | Temporary Items 284 | .apdisk 285 | ### Xcode template 286 | # Xcode 287 | # 288 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 289 | 290 | ## Build generated 291 | build/ 292 | DerivedData 293 | 294 | ## Various settings 295 | *.pbxuser 296 | !default.pbxuser 297 | *.mode1v3 298 | !default.mode1v3 299 | *.mode2v3 300 | !default.mode2v3 301 | *.perspectivev3 302 | !default.perspectivev3 303 | xcuserdata 304 | 305 | ## Other 306 | *.xccheckout 307 | *.moved-aside 308 | *.xcuserstate 309 | ### Scala template 310 | *.class 311 | *.log 312 | /.bsp 313 | 314 | # sbt specific 315 | .cache 316 | .history 317 | .lib/ 318 | dist/* 319 | target/ 320 | lib_managed/ 321 | src_managed/ 322 | project/boot/ 323 | project/plugins/project/ 324 | 325 | # Scala-IDE specific 326 | .scala_dependencies 327 | .worksheet 328 | ### Java template 329 | *.class 330 | 331 | # Mobile Tools for Java (J2ME) 332 | .mtj.tmp/ 333 | 334 | # Package Files # 335 | *.jar 336 | *.war 337 | *.ear 338 | 339 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 340 | hs_err_pid* 341 | 342 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HDMI Chisel core 2 | ================ 3 | 4 | This HDMI core is mainly inspired from 5 | [vhdl-hdmi-out](https://github.com/fcayci/vhdl-hdmi-out) project. 6 | 7 | ![French_flags_720p](assets/ff720p.jpg) 8 | 9 | 10 | 11 | 12 | # Install 13 | 14 | To use it locally, first clone the repository then publish it locally : 15 | 16 | ```Shell 17 | $ git clone https://github.com/Martoni/HdmiCore.git 18 | $ cd HdmiCore 19 | $ sbt publishLocal 20 | ``` 21 | 22 | HdmiCore use fpgamacro project to work. It must be published locally too : 23 | 24 | ```Shell 25 | $ git clone https://github.com/Martoni/fpgamacro.git 26 | $ cd fpgamacro 27 | $ sbt publishLocal 28 | ``` 29 | 30 | 31 | On your personnal `build.sbt` project add the following line : 32 | 33 | ```Scala 34 | libraryDependencies ++= Seq( 35 | // ... 36 | "com.armadeus" %% "hdmicore" % "6.2.0" 37 | ), 38 | 39 | ``` 40 | 41 | # Generate simple pattern 42 | 43 | ## For TangNano4k 44 | 45 | First launch sbt then generate simple pattern for [Tang Nano 4K board](http://www.fabienm.eu/flf/reception-du-kit-tang-nano-4k/): 46 | 47 | ```Shell 48 | $ sbt 49 | sbt:hdmicore> runMain hdmicore.platforms.TangNano4k 50 | [info] running hdmicore.platforms.TangNano4k 51 | Elaborating design... 52 | H_DISPLAY UInt<11>(1280) 53 | V_DISPLAY UInt<10>(720) 54 | hregsize 11 55 | vregsize 10 56 | Done elaborating. 57 | ``` 58 | 59 | A file named `TangNano4k.v` should be generated, it's the verilog file for the 60 | entire project (the TOP component) 61 | 62 | Then launch gowin_ide and create a project with fpga part named `GW1NSR-LV4CQN48PC7/I6`. 63 | Once project created do not forget to activate `MODE` pin as a standard IO in menu 64 | `Project->Configuration->Dual-Purpose Pin` and select `use MODE as regular IO`. 65 | It's for led blinking :) 66 | 67 | Then finaly add these files (prefere to not copy in local directory): 68 | 69 | ``` 70 | TangNano4k.v 71 | platforms/tangnano4k/tangnano4k.cst 72 | platforms/tangnano4k/tangnano4k.sdc 73 | ``` 74 | 75 | Then launch synthesis and place&route 76 | 77 | Finally flash your board with [openFPGALoader](https://github.com/trabucayre/openFPGALoader): 78 | 79 | ```Shell 80 | $ openFPGALoader ide/tangnano4khdmi/impl/pnr/tangnano4khdmi.fs 81 | write to ram 82 | Jtag frequency : requested 6.00MHz -> real 6.00MHz 83 | Parse file Parse ide/tangnano4khdmi/impl/pnr/tangnano4khdmi.fs: 84 | Done 85 | DONE 86 | Jtag frequency : requested 2.50MHz -> real 2.00MHz 87 | erase SRAM Done 88 | Flash SRAM: [==================================================] 100.00% 89 | Done 90 | SRAM Flash: Success 91 | ``` 92 | 93 | Plug HDMI then enjoy the result. 94 | 95 | 96 | ## For TangNano9k 97 | 98 | First launch sbt then generate simple pattern for [Tang Nano 9K board](http://www.fabienm.eu/flf/deballage-de-la-tangnano9k/): 99 | 100 | ```Shell 101 | $ sbt 102 | sbt:hdmicore> runMain hdmicore.platforms.TangNano9k 103 | [info] running hdmicore.platforms.TangNano9k 104 | Elaborating design... 105 | H_DISPLAY UInt<11>(1280) 106 | V_DISPLAY UInt<10>(720) 107 | hregsize 11 108 | vregsize 10 109 | Done elaborating. 110 | ``` 111 | 112 | A file named `TangNano9k.v` should be generated, it's the verilog file for the 113 | entire project (the TOP component) 114 | 115 | Then launch gowin_ide and create a project with fpga part named `GW1NSR-LV4CQN48PC7/I6`. 116 | Once project created do not forget to activate `MODE` pin as a standard IO in menu 117 | `Project->Configuration->Dual-Purpose Pin` and select `use MODE as regular IO`. 118 | It's for led blinking :) 119 | 120 | Then finaly add these files (prefere to not copy in local directory): 121 | 122 | ``` 123 | TangNano4k.v 124 | platforms/tangnano9k/tangnano9k.cst 125 | platforms/tangnano9k/tangnano9k.sdc 126 | ``` 127 | 128 | Then launch synthesis and place&route 129 | 130 | Finally flash your board with [openFPGALoader](https://github.com/trabucayre/openFPGALoader): 131 | 132 | ```Shell 133 | $ openFPGALoader ide/tangnano4khdmi/impl/pnr/tangnano9khdmi.fs 134 | write to ram 135 | Jtag frequency : requested 6.00MHz -> real 6.00MHz 136 | Parse file Parse ide/tangnano4khdmi/impl/pnr/tangnano9khdmi.fs: 137 | Done 138 | DONE 139 | Jtag frequency : requested 2.50MHz -> real 2.00MHz 140 | erase SRAM Done 141 | Flash SRAM: [==================================================] 100.00% 142 | Done 143 | SRAM Flash: Success 144 | ``` 145 | 146 | Plug HDMI then enjoy the result. 147 | -------------------------------------------------------------------------------- /assets/ff720p.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/assets/ff720p.jpg -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | // See README.md for license details. 2 | 3 | val majorChiselVersion = "6" 4 | val minorChiselVersion = "2.0" 5 | 6 | val chiselVersion = majorChiselVersion + "." + minorChiselVersion 7 | 8 | scalaVersion := "2.13.8" 9 | version := chiselVersion 10 | organization := "eu.fabienm" 11 | 12 | lazy val root = (project in file(".")) 13 | .settings( 14 | name := "hdmicore", 15 | libraryDependencies ++= Seq( 16 | "org.chipsalliance" %% "chisel" % chiselVersion, 17 | "Martoni" %% "fpgamacro" % "6.2.1" 18 | ), 19 | scalacOptions ++= Seq( 20 | "-language:reflectiveCalls", 21 | "-deprecation", 22 | "-feature", 23 | "-Xcheckinit", 24 | "-Ymacro-annotations", 25 | ), 26 | addCompilerPlugin("org.chipsalliance" % "chisel-plugin" % chiselVersion cross CrossVersion.full), 27 | ) 28 | -------------------------------------------------------------------------------- /build.sc: -------------------------------------------------------------------------------- 1 | // import Mill dependency 2 | import mill._ 3 | import mill.define.Sources 4 | import mill.modules.Util 5 | import mill.scalalib.TestModule.ScalaTest 6 | import scalalib._ 7 | // support BSP 8 | import mill.bsp._ 9 | 10 | object HDMI extends SbtModule { m => 11 | override def millSourcePath = os.pwd 12 | override def scalaVersion = "2.12.13" 13 | override def scalacOptions = Seq( 14 | "-Xsource:2.11", 15 | "-language:reflectiveCalls", 16 | "-deprecation", 17 | "-feature", 18 | "-Xcheckinit", 19 | ) 20 | override def ivyDeps = Agg( 21 | ivy"edu.berkeley.cs::chisel3:3.5.0-RC1", 22 | ) 23 | override def scalacPluginIvyDeps = Agg( 24 | ivy"edu.berkeley.cs:::chisel3-plugin:3.5.0-RC1", 25 | ivy"org.scalamacros:::paradise:2.1.1" 26 | ) 27 | object test extends Tests with ScalaTest { 28 | override def ivyDeps = m.ivyDeps() ++ Agg( 29 | ivy"edu.berkeley.cs::chiseltest:0.5.0-RC1" 30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /generate_all_verilog_sources.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | sbt "runMain hdmicore.Rgb2Tmds" 4 | sbt "runMain hdmicore.TMDSEncoder" 5 | sbt "runMain hdmicore.platforms.TangNano4k" 6 | sbt "runMain hdmicore.platforms.TangNano9k" 7 | sbt "runMain hdmicore.platforms.Ulx3s" 8 | sbt "runMain hdmicore.video.HVSyncDriver" 9 | -------------------------------------------------------------------------------- /platforms/tangnano4k/tangnano4k.cst: -------------------------------------------------------------------------------- 1 | //Copyright (C)2014-2020 Gowin Semiconductor Corporation. 2 | //All rights reserved. 3 | //File Title: Physical Constraints file 4 | //GOWIN Version: 1.9.7.01Beta 5 | //Part Number: GW1NSR-LV4CQN48PC7/I6 6 | //Device: GW1NSR-4C 7 | //Created Time: Fri 06 11 14:54:37 2021 8 | 9 | IO_LOC "O_tmds_clk_p" 28,27; 10 | IO_PORT "O_tmds_clk_p" PULL_MODE=NONE DRIVE=3.5; 11 | IO_LOC "O_tmds_data_0_p" 30,29; 12 | IO_PORT "O_tmds_data_0_p" PULL_MODE=NONE DRIVE=3.5; 13 | IO_LOC "O_tmds_data_1_p" 32,31; 14 | IO_PORT "O_tmds_data_1_p" PULL_MODE=NONE DRIVE=3.5; 15 | IO_LOC "O_tmds_data_2_p" 35,34; 16 | IO_PORT "O_tmds_data_2_p" PULL_MODE=NONE DRIVE=3.5; 17 | 18 | IO_LOC "O_led[0]" 10; 19 | IO_PORT "O_led[0]" IO_TYPE=LVCMOS33 PULL_MODE=NONE DRIVE=8; 20 | 21 | IO_LOC "I_reset_n" 14; 22 | IO_PORT "I_reset_n" PULL_MODE=UP; 23 | IO_LOC "I_clk" 45; 24 | IO_PORT "I_clk" IO_TYPE=LVCMOS33 PULL_MODE=UP; 25 | 26 | IO_LOC "I_button" 15; 27 | IO_PORT "I_button" IO_TYPE=LVCMOS33 PULL_MODE=UP; 28 | 29 | -------------------------------------------------------------------------------- /platforms/tangnano4k/tangnano4k.sdc: -------------------------------------------------------------------------------- 1 | //Copyright (C)2014-2021 GOWIN Semiconductor Corporation. 2 | //All rights reserved. 3 | //File Title: Timing Constraints file 4 | //GOWIN Version: 1.9.7.02 Beta 5 | //Created Time: 2021-06-01 10:34:02 6 | create_clock -name I_clk -period 37.037 -waveform {0 18.518} [get_ports {I_clk}] -add 7 | create_clock -name serial_clk -period 2.694 -waveform {0 1.347} [get_nets {tmdsPllvr_io_clkout}] -add 8 | create_clock -name pix_clk -period 13.468 -waveform {0 6.734} [get_nets {clkDiv_CLKOUT}] -add 9 | -------------------------------------------------------------------------------- /platforms/tangnano9k/TMDS_PLLVR.v: -------------------------------------------------------------------------------- 1 | //Copyright (C)2014-2020 Gowin Semiconductor Corporation. 2 | //All rights reserved. 3 | //File Title: IP file 4 | //GOWIN Version: V1.9.7.01Beta 5 | //Part Number: GW1NSR-LV4CQN48PC7/I6 6 | //Device: GW1NSR-4C 7 | //Created Time: Tue Nov 03 14:39:26 2020 8 | 9 | module TMDS_PLLVR (clkout, lock, clkoutd, clkin); 10 | 11 | output clkout; 12 | output lock; 13 | output clkoutd; 14 | input clkin; 15 | 16 | wire clkoutp_o; 17 | wire clkoutd3_o; 18 | wire gw_vcc; 19 | wire gw_gnd; 20 | 21 | assign gw_vcc = 1'b1; 22 | assign gw_gnd = 1'b0; 23 | 24 | PLLVR pllvr_inst ( 25 | .CLKOUT(clkout), 26 | .LOCK(lock), 27 | .CLKOUTP(clkoutp_o), 28 | .CLKOUTD(clkoutd), 29 | .CLKOUTD3(clkoutd3_o), 30 | .RESET(gw_gnd), 31 | .RESET_P(gw_gnd), 32 | .CLKIN(clkin), 33 | .CLKFB(gw_gnd), 34 | .FBDSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}), 35 | .IDSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}), 36 | .ODSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}), 37 | .PSDA({gw_gnd,gw_gnd,gw_gnd,gw_gnd}), 38 | .DUTYDA({gw_gnd,gw_gnd,gw_gnd,gw_gnd}), 39 | .FDLY({gw_gnd,gw_gnd,gw_gnd,gw_gnd}), 40 | .VREN(gw_vcc) 41 | ); 42 | 43 | defparam pllvr_inst.FCLKIN = "27"; 44 | defparam pllvr_inst.DYN_IDIV_SEL = "false"; 45 | defparam pllvr_inst.IDIV_SEL = 3; 46 | defparam pllvr_inst.DYN_FBDIV_SEL = "false"; 47 | defparam pllvr_inst.FBDIV_SEL = 54; 48 | defparam pllvr_inst.DYN_ODIV_SEL = "false"; 49 | defparam pllvr_inst.ODIV_SEL = 2; 50 | defparam pllvr_inst.PSDA_SEL = "0000"; 51 | defparam pllvr_inst.DYN_DA_EN = "false"; 52 | defparam pllvr_inst.DUTYDA_SEL = "1000"; 53 | defparam pllvr_inst.CLKOUT_FT_DIR = 1'b1; 54 | defparam pllvr_inst.CLKOUTP_FT_DIR = 1'b1; 55 | defparam pllvr_inst.CLKOUT_DLY_STEP = 0; 56 | defparam pllvr_inst.CLKOUTP_DLY_STEP = 0; 57 | defparam pllvr_inst.CLKFB_SEL = "internal"; 58 | defparam pllvr_inst.CLKOUT_BYPASS = "false"; 59 | defparam pllvr_inst.CLKOUTP_BYPASS = "false"; 60 | defparam pllvr_inst.CLKOUTD_BYPASS = "false"; 61 | defparam pllvr_inst.DYN_SDIV_SEL = 30; 62 | defparam pllvr_inst.CLKOUTD_SRC = "CLKOUT"; 63 | defparam pllvr_inst.CLKOUTD3_SRC = "CLKOUT"; 64 | defparam pllvr_inst.DEVICE = "GW1NSR-4C"; 65 | 66 | endmodule //TMDS_PLLVR 67 | -------------------------------------------------------------------------------- /platforms/tangnano9k/gowin_rpll.v: -------------------------------------------------------------------------------- 1 | //Copyright (C)2014-2022 Gowin Semiconductor Corporation. 2 | //All rights reserved. 3 | //File Title: IP file 4 | //GOWIN Version: V1.9.8.03 5 | //Part Number: GW1NR-LV9QN88PC6/I5 6 | //Device: GW1NR-9 7 | //Created Time: Tue Mar 8 21:04:34 2022 8 | 9 | module Gowin_rPLL (clkout, lock, clkin); 10 | 11 | output clkout; 12 | output lock; 13 | input clkin; 14 | 15 | wire clkoutp_o; 16 | wire clkoutd_o; 17 | wire clkoutd3_o; 18 | wire gw_gnd; 19 | 20 | assign gw_gnd = 1'b0; 21 | 22 | rPLL rpll_inst ( 23 | .CLKOUT(clkout), 24 | .LOCK(lock), 25 | .CLKOUTP(clkoutp_o), 26 | .CLKOUTD(clkoutd_o), 27 | .CLKOUTD3(clkoutd3_o), 28 | .RESET(gw_gnd), 29 | .RESET_P(gw_gnd), 30 | .CLKIN(clkin), 31 | .CLKFB(gw_gnd), 32 | .FBDSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}), 33 | .IDSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}), 34 | .ODSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}), 35 | .PSDA({gw_gnd,gw_gnd,gw_gnd,gw_gnd}), 36 | .DUTYDA({gw_gnd,gw_gnd,gw_gnd,gw_gnd}), 37 | .FDLY({gw_gnd,gw_gnd,gw_gnd,gw_gnd}) 38 | ); 39 | 40 | defparam rpll_inst.FCLKIN = "27"; 41 | defparam rpll_inst.DYN_IDIV_SEL = "false"; 42 | defparam rpll_inst.IDIV_SEL = 3; 43 | defparam rpll_inst.DYN_FBDIV_SEL = "false"; 44 | defparam rpll_inst.FBDIV_SEL = 54; 45 | defparam rpll_inst.DYN_ODIV_SEL = "false"; 46 | defparam rpll_inst.ODIV_SEL = 2; 47 | defparam rpll_inst.PSDA_SEL = "0000"; 48 | defparam rpll_inst.DYN_DA_EN = "true"; 49 | defparam rpll_inst.DUTYDA_SEL = "1000"; 50 | defparam rpll_inst.CLKOUT_FT_DIR = 1'b1; 51 | defparam rpll_inst.CLKOUTP_FT_DIR = 1'b1; 52 | defparam rpll_inst.CLKOUT_DLY_STEP = 0; 53 | defparam rpll_inst.CLKOUTP_DLY_STEP = 0; 54 | defparam rpll_inst.CLKFB_SEL = "internal"; 55 | defparam rpll_inst.CLKOUT_BYPASS = "false"; 56 | defparam rpll_inst.CLKOUTP_BYPASS = "false"; 57 | defparam rpll_inst.CLKOUTD_BYPASS = "false"; 58 | defparam rpll_inst.DYN_SDIV_SEL = 2; 59 | defparam rpll_inst.CLKOUTD_SRC = "CLKOUT"; 60 | defparam rpll_inst.CLKOUTD3_SRC = "CLKOUT"; 61 | defparam rpll_inst.DEVICE = "GW1NR-9"; 62 | 63 | endmodule //Gowin_rPLL 64 | -------------------------------------------------------------------------------- /platforms/tangnano9k/tangnano9k.cst: -------------------------------------------------------------------------------- 1 | //GOWIN Version: 1.9.8.03 2 | //Part Number: GW1NR-LV9QN88PC6/I5 3 | //Device: GW1NR-9 4 | 5 | IO_LOC "O_tmds_clk_p" 69,68; 6 | IO_PORT "O_tmds_clk_p" PULL_MODE=NONE DRIVE=8; 7 | IO_LOC "O_tmds_data_0_p" 71,70; 8 | IO_PORT "O_tmds_data_0_p" PULL_MODE=NONE DRIVE=8; 9 | IO_LOC "O_tmds_data_1_p" 73,72; 10 | IO_PORT "O_tmds_data_1_p" PULL_MODE=NONE DRIVE=8; 11 | IO_LOC "O_tmds_data_2_p" 75,74; 12 | IO_PORT "O_tmds_data_2_p" PULL_MODE=NONE DRIVE=8; 13 | 14 | IO_LOC "O_led[0]" 10; 15 | IO_PORT "O_led[0]" IO_TYPE=LVCMOS33 PULL_MODE=NONE DRIVE=8; 16 | 17 | IO_LOC "I_reset_n" 4; 18 | IO_PORT "I_reset_n" PULL_MODE=UP; 19 | IO_LOC "I_clk" 52; 20 | IO_PORT "I_clk" IO_TYPE=LVCMOS33 PULL_MODE=UP; 21 | 22 | IO_LOC "I_button" 3; 23 | IO_PORT "I_button" IO_TYPE=LVCMOS33 PULL_MODE=UP; 24 | 25 | -------------------------------------------------------------------------------- /platforms/tangnano9k/tangnano9k.sdc: -------------------------------------------------------------------------------- 1 | //Copyright (C)2014-2021 GOWIN Semiconductor Corporation. 2 | //All rights reserved. 3 | //File Title: Timing Constraints file 4 | //GOWIN Version: 1.9.7.02 Beta 5 | //Created Time: 2021-06-01 10:34:02 6 | create_clock -name I_clk -period 37.037 -waveform {0 18.518} [get_ports {I_clk}] -add 7 | create_clock -name serial_clk -period 2.694 -waveform {0 1.347} [get_nets {tmdsPllvr_io_clkout}] -add 8 | create_clock -name pix_clk -period 13.468 -waveform {0 6.734} [get_nets {clkDiv_CLKOUT}] -add 9 | -------------------------------------------------------------------------------- /platforms/ulx3s/ulx3s_v20.lpf: -------------------------------------------------------------------------------- 1 | BLOCK RESETPATHS; 2 | BLOCK ASYNCPATHS; 3 | ## ULX3S v2.x.x and v3.0.x 4 | ## from https://github.com/splinedrive/my_hdmi_device/blob/master/ulx3s_v20.lpf 5 | 6 | # The clock "usb" and "gpdi" sheet 7 | LOCATE COMP "clk_25mhz" SITE "G2"; 8 | IOBUF PORT "clk_25mhz" PULLMODE=NONE IO_TYPE=LVCMOS33; 9 | FREQUENCY PORT "clk_25mhz" 25 MHZ; 10 | 11 | # JTAG and SPI FLASH voltage 3.3V and options to boot from SPI flash 12 | # write to FLASH possible any time from JTAG: 13 | SYSCONFIG CONFIG_IOVOLTAGE=3.3 COMPRESS_CONFIG=ON MCCLK_FREQ=62 SLAVE_SPI_PORT=DISABLE MASTER_SPI_PORT=ENABLE SLAVE_PARALLEL_PORT=DISABLE; 14 | # write to FLASH possible from user bitstream: 15 | # SYSCONFIG CONFIG_IOVOLTAGE=3.3 COMPRESS_CONFIG=ON MCCLK_FREQ=62 SLAVE_SPI_PORT=DISABLE MASTER_SPI_PORT=DISABLE SLAVE_PARALLEL_PORT=DISABLE; 16 | 17 | ## USBSERIAL FTDI-FPGA serial port "usb" sheet 18 | LOCATE COMP "ftdi_rxd" SITE "L4"; # FPGA transmits to ftdi 19 | LOCATE COMP "ftdi_txd" SITE "M1"; # FPGA receives from ftdi 20 | LOCATE COMP "ftdi_nrts" SITE "M3"; # FPGA receives 21 | LOCATE COMP "ftdi_ndtr" SITE "N1"; # FPGA receives 22 | LOCATE COMP "ftdi_txden" SITE "L3"; # FPGA receives 23 | IOBUF PORT "ftdi_rxd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 24 | IOBUF PORT "ftdi_txd" PULLMODE=UP IO_TYPE=LVCMOS33; 25 | IOBUF PORT "ftdi_nrts" PULLMODE=UP IO_TYPE=LVCMOS33; 26 | IOBUF PORT "ftdi_ndtr" PULLMODE=UP IO_TYPE=LVCMOS33; 27 | IOBUF PORT "ftdi_txden" PULLMODE=UP IO_TYPE=LVCMOS33; 28 | 29 | ## LED indicators "blinkey" and "gpio" sheet 30 | LOCATE COMP "led[7]" SITE "H3"; 31 | LOCATE COMP "led[6]" SITE "E1"; 32 | LOCATE COMP "led[5]" SITE "E2"; 33 | LOCATE COMP "led[4]" SITE "D1"; 34 | LOCATE COMP "led[3]" SITE "D2"; 35 | LOCATE COMP "led[2]" SITE "C1"; 36 | LOCATE COMP "led[1]" SITE "C2"; 37 | LOCATE COMP "led[0]" SITE "B2"; 38 | IOBUF PORT "led[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 39 | IOBUF PORT "led[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 40 | IOBUF PORT "led[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 41 | IOBUF PORT "led[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 42 | IOBUF PORT "led[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 43 | IOBUF PORT "led[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 44 | IOBUF PORT "led[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 45 | IOBUF PORT "led[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 46 | 47 | ## Pushbuttons "blinkey", "flash", "power", "gpdi" sheet 48 | LOCATE COMP "btn[0]" SITE "D6"; # BTN_PWRn (inverted logic) 49 | LOCATE COMP "btn[1]" SITE "R1"; # FIRE1 50 | LOCATE COMP "btn[2]" SITE "T1"; # FIRE2 51 | LOCATE COMP "btn[3]" SITE "R18"; # UP W1->R18 52 | LOCATE COMP "btn[4]" SITE "V1"; # DOWN 53 | LOCATE COMP "btn[5]" SITE "U1"; # LEFT 54 | LOCATE COMP "btn[6]" SITE "H16"; # RIGHT Y2->H16 55 | IOBUF PORT "btn[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 56 | IOBUF PORT "btn[1]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 57 | IOBUF PORT "btn[2]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 58 | IOBUF PORT "btn[3]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 59 | IOBUF PORT "btn[4]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 60 | IOBUF PORT "btn[5]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 61 | IOBUF PORT "btn[6]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 62 | 63 | ## DIP switch "blinkey", "gpio" sheet 64 | LOCATE COMP "sw[0]" SITE "E8"; # SW1 65 | LOCATE COMP "sw[1]" SITE "D8"; # SW2 66 | LOCATE COMP "sw[2]" SITE "D7"; # SW3 67 | LOCATE COMP "sw[3]" SITE "E7"; # SW4 68 | IOBUF PORT "sw[0]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 69 | IOBUF PORT "sw[1]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 70 | IOBUF PORT "sw[2]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 71 | IOBUF PORT "sw[3]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 72 | 73 | ## SPI OLED DISPLAY SSD1331 (Color) or SSD1306 (B/W) "blinkey", "usb" sheet 74 | LOCATE COMP "oled_clk" SITE "P4"; 75 | LOCATE COMP "oled_mosi" SITE "P3"; 76 | LOCATE COMP "oled_dc" SITE "P1"; 77 | LOCATE COMP "oled_resn" SITE "P2"; 78 | LOCATE COMP "oled_csn" SITE "N2"; 79 | IOBUF PORT "oled_clk" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 80 | IOBUF PORT "oled_mosi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 81 | IOBUF PORT "oled_dc" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 82 | IOBUF PORT "oled_resn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 83 | IOBUF PORT "oled_csn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 84 | 85 | ## SPI Flash chip "flash" sheet 86 | LOCATE COMP "flash_csn" SITE "R2"; 87 | LOCATE COMP "flash_clk" SITE "U3"; 88 | LOCATE COMP "flash_mosi" SITE "W2"; 89 | LOCATE COMP "flash_miso" SITE "V2"; 90 | LOCATE COMP "flash_holdn" SITE "W1"; 91 | LOCATE COMP "flash_wpn" SITE "Y2"; 92 | #LOCATE COMP "flash_csspin" SITE "AJ3"; 93 | #LOCATE COMP "flash_initn" SITE "AG4"; 94 | #LOCATE COMP "flash_done" SITE "AJ4"; 95 | #LOCATE COMP "flash_programn" SITE "AH4"; 96 | #LOCATE COMP "flash_cfg_select[0]" SITE "AM4"; 97 | #LOCATE COMP "flash_cfg_select[1]" SITE "AL4"; 98 | #LOCATE COMP "flash_cfg_select[2]" SITE "AK4"; 99 | IOBUF PORT "flash_csn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 100 | IOBUF PORT "flash_clk" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 101 | IOBUF PORT "flash_mosi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 102 | IOBUF PORT "flash_miso" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 103 | IOBUF PORT "flash_holdn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 104 | IOBUF PORT "flash_wpn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 105 | #IOBUF PORT "flash_csspin" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 106 | #IOBUF PORT "flash_initn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 107 | #IOBUF PORT "flash_done" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 108 | #IOBUF PORT "flash_programn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 109 | #IOBUF PORT "flash_cfg_select[0]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 110 | #IOBUF PORT "flash_cfg_select[1]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 111 | #IOBUF PORT "flash_cfg_select[2]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 112 | 113 | ## SD card "sdcard", "usb" sheet 114 | LOCATE COMP "sd_clk" SITE "H2"; # sd_clk WiFi_GPIO14 115 | LOCATE COMP "sd_cmd" SITE "J1"; # sd_cmd_di (MOSI) WiFi GPIO15 116 | LOCATE COMP "sd_d[0]" SITE "J3"; # sd_dat0_do (MISO) WiFi GPIO2 117 | LOCATE COMP "sd_d[1]" SITE "H1"; # sd_dat1_irq WiFi GPIO4 118 | LOCATE COMP "sd_d[2]" SITE "K1"; # sd_dat2 WiFi_GPIO12 119 | LOCATE COMP "sd_d[3]" SITE "K2"; # sd_dat3_csn WiFi_GPIO13 120 | LOCATE COMP "sd_wp" SITE "P5"; # not connected 121 | LOCATE COMP "sd_cdn" SITE "N5"; # not connected 122 | IOBUF PORT "sd_clk" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 123 | IOBUF PORT "sd_cmd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 124 | IOBUF PORT "sd_d[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 125 | IOBUF PORT "sd_d[1]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 126 | IOBUF PORT "sd_d[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; # WiFi GPIO12 pulldown bootstrapping requirement 127 | IOBUF PORT "sd_d[3]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 128 | IOBUF PORT "sd_wp" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 129 | IOBUF PORT "sd_cdn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 130 | 131 | ## ADC SPI (MAX11123) "analog", "ram" sheet 132 | LOCATE COMP "adc_csn" SITE "R17"; 133 | LOCATE COMP "adc_mosi" SITE "R16"; 134 | LOCATE COMP "adc_miso" SITE "U16"; 135 | LOCATE COMP "adc_sclk" SITE "P17"; 136 | IOBUF PORT "adc_csn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 137 | IOBUF PORT "adc_mosi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 138 | IOBUF PORT "adc_miso" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 139 | IOBUF PORT "adc_sclk" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 140 | 141 | ## Audio 4-bit DAC "analog", "gpio" sheet 142 | # 4-bit mode can drive down to 75 ohm load impedance. 143 | # Lower impedance leads to IO overload, 144 | # FPGA will stop working and need reboot. 145 | # For standard 17 ohm earphones on PCB v1.7: 146 | # use bits 2,3 as input (High-Z) and drive only bits 0,1. 147 | # PCB v2.1.2 can use full 4 bits and 16mA drive for 17 ohm earphones. 148 | LOCATE COMP "audio_l[3]" SITE "B3"; # JACK TIP (left audio) 149 | LOCATE COMP "audio_l[2]" SITE "C3"; 150 | LOCATE COMP "audio_l[1]" SITE "D3"; 151 | LOCATE COMP "audio_l[0]" SITE "E4"; 152 | LOCATE COMP "audio_r[3]" SITE "C5"; # JACK RING1 (right audio) 153 | LOCATE COMP "audio_r[2]" SITE "D5"; 154 | LOCATE COMP "audio_r[1]" SITE "B5"; 155 | LOCATE COMP "audio_r[0]" SITE "A3"; 156 | LOCATE COMP "audio_v[3]" SITE "E5"; # JACK RING2 (video or digital audio) 157 | LOCATE COMP "audio_v[2]" SITE "F5"; 158 | LOCATE COMP "audio_v[1]" SITE "F2"; 159 | LOCATE COMP "audio_v[0]" SITE "H5"; 160 | IOBUF PORT "audio_l[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 161 | IOBUF PORT "audio_l[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 162 | IOBUF PORT "audio_l[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 163 | IOBUF PORT "audio_l[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 164 | IOBUF PORT "audio_r[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 165 | IOBUF PORT "audio_r[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 166 | IOBUF PORT "audio_r[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 167 | IOBUF PORT "audio_r[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 168 | IOBUF PORT "audio_v[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 169 | IOBUF PORT "audio_v[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 170 | IOBUF PORT "audio_v[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 171 | IOBUF PORT "audio_v[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 172 | 173 | ## WiFi ESP-32 "wifi", "usb", "flash" sheet 174 | # other pins are shared with GP/GN, SD card and JTAG 175 | LOCATE COMP "wifi_en" SITE "F1"; # enable/reset WiFi 176 | LOCATE COMP "wifi_rxd" SITE "K3"; # FPGA transmits to WiFi 177 | LOCATE COMP "wifi_txd" SITE "K4"; # FPGA receives from WiFi 178 | LOCATE COMP "wifi_gpio0" SITE "L2"; 179 | LOCATE COMP "wifi_gpio5" SITE "N4"; # WIFI LED 180 | LOCATE COMP "wifi_gpio16" SITE "L1"; # Serial1 RX 181 | LOCATE COMP "wifi_gpio17" SITE "N3"; # Serial1 TX 182 | # LOCATE COMP "prog_done" SITE "Y3"; # not GPIO, always active 183 | IOBUF PORT "wifi_en" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 184 | IOBUF PORT "wifi_rxd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 185 | IOBUF PORT "wifi_txd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 186 | IOBUF PORT "wifi_gpio0" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 187 | IOBUF PORT "wifi_gpio5" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 188 | IOBUF PORT "wifi_gpio16" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 189 | IOBUF PORT "wifi_gpio17" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 190 | # IOBUF PORT "prog_done" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 191 | 192 | ## PCB antenna 433 MHz (may be also used for FM) "usb" sheet 193 | LOCATE COMP "ant_433mhz" SITE "G1"; 194 | IOBUF PORT "ant_433mhz" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 195 | 196 | ## Second USB port "US2" going directly into FPGA "usb", "ram" sheet 197 | LOCATE COMP "usb_fpga_dp" SITE "E16"; # single ended or differential input only 198 | LOCATE COMP "usb_fpga_dn" SITE "F16"; 199 | IOBUF PORT "usb_fpga_dp" PULLMODE=NONE IO_TYPE=LVCMOS33D DRIVE=16; 200 | IOBUF PORT "usb_fpga_dn" PULLMODE=NONE IO_TYPE=LVCMOS33D DRIVE=16; 201 | LOCATE COMP "usb_fpga_bd_dp" SITE "D15"; # single-ended bidirectional 202 | LOCATE COMP "usb_fpga_bd_dn" SITE "E15"; 203 | IOBUF PORT "usb_fpga_bd_dp" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 204 | IOBUF PORT "usb_fpga_bd_dn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 205 | LOCATE COMP "usb_fpga_pu_dp" SITE "B12"; # pull up/down control 206 | LOCATE COMP "usb_fpga_pu_dn" SITE "C12"; 207 | IOBUF PORT "usb_fpga_pu_dp" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 208 | IOBUF PORT "usb_fpga_pu_dn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; 209 | 210 | ## JTAG ESP-32 "usb" sheet 211 | # connected to FT231X and ESP-32 212 | # commented out because those are dedicated pins, not directly useable as GPIO 213 | # but could be used by some vendor-specific JTAG bridging (boundary scan) module 214 | #LOCATE COMP "jtag_tdi" SITE "R5"; # FTDI_nRI FPGA receives 215 | #LOCATE COMP "jtag_tdo" SITE "V4"; # FTDI_nCTS FPGA transmits 216 | #LOCATE COMP "jtag_tck" SITE "T5"; # FTDI_nDSR FPGA receives 217 | #LOCATE COMP "jtag_tms" SITE "U5"; # FTDI_nDCD FPGA receives 218 | #IOBUF PORT "jtag_tdi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 219 | #IOBUF PORT "jtag_tdo" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 220 | #IOBUF PORT "jtag_tck" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 221 | #IOBUF PORT "jtag_tms" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 222 | 223 | ## SDRAM "ram" sheet 224 | LOCATE COMP "sdram_clk" SITE "F19"; 225 | LOCATE COMP "sdram_cke" SITE "F20"; 226 | LOCATE COMP "sdram_csn" SITE "P20"; 227 | LOCATE COMP "sdram_wen" SITE "T20"; 228 | LOCATE COMP "sdram_rasn" SITE "R20"; 229 | LOCATE COMP "sdram_casn" SITE "T19"; 230 | LOCATE COMP "sdram_a[0]" SITE "M20"; 231 | LOCATE COMP "sdram_a[1]" SITE "M19"; 232 | LOCATE COMP "sdram_a[2]" SITE "L20"; 233 | LOCATE COMP "sdram_a[3]" SITE "L19"; 234 | LOCATE COMP "sdram_a[4]" SITE "K20"; 235 | LOCATE COMP "sdram_a[5]" SITE "K19"; 236 | LOCATE COMP "sdram_a[6]" SITE "K18"; 237 | LOCATE COMP "sdram_a[7]" SITE "J20"; 238 | LOCATE COMP "sdram_a[8]" SITE "J19"; 239 | LOCATE COMP "sdram_a[9]" SITE "H20"; 240 | LOCATE COMP "sdram_a[10]" SITE "N19"; 241 | LOCATE COMP "sdram_a[11]" SITE "G20"; 242 | LOCATE COMP "sdram_a[12]" SITE "G19"; 243 | LOCATE COMP "sdram_ba[0]" SITE "P19"; 244 | LOCATE COMP "sdram_ba[1]" SITE "N20"; 245 | LOCATE COMP "sdram_dqm[0]" SITE "U19"; 246 | LOCATE COMP "sdram_dqm[1]" SITE "E20"; 247 | LOCATE COMP "sdram_d[0]" SITE "J16"; 248 | LOCATE COMP "sdram_d[1]" SITE "L18"; 249 | LOCATE COMP "sdram_d[2]" SITE "M18"; 250 | LOCATE COMP "sdram_d[3]" SITE "N18"; 251 | LOCATE COMP "sdram_d[4]" SITE "P18"; 252 | LOCATE COMP "sdram_d[5]" SITE "T18"; 253 | LOCATE COMP "sdram_d[6]" SITE "T17"; 254 | LOCATE COMP "sdram_d[7]" SITE "U20"; 255 | LOCATE COMP "sdram_d[8]" SITE "E19"; 256 | LOCATE COMP "sdram_d[9]" SITE "D20"; 257 | LOCATE COMP "sdram_d[10]" SITE "D19"; 258 | LOCATE COMP "sdram_d[11]" SITE "C20"; 259 | LOCATE COMP "sdram_d[12]" SITE "E18"; 260 | LOCATE COMP "sdram_d[13]" SITE "F18"; 261 | LOCATE COMP "sdram_d[14]" SITE "J18"; 262 | LOCATE COMP "sdram_d[15]" SITE "J17"; 263 | IOBUF PORT "sdram_clk" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 264 | IOBUF PORT "sdram_cke" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 265 | IOBUF PORT "sdram_csn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 266 | IOBUF PORT "sdram_wen" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 267 | IOBUF PORT "sdram_rasn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 268 | IOBUF PORT "sdram_casn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 269 | IOBUF PORT "sdram_a[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 270 | IOBUF PORT "sdram_a[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 271 | IOBUF PORT "sdram_a[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 272 | IOBUF PORT "sdram_a[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 273 | IOBUF PORT "sdram_a[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 274 | IOBUF PORT "sdram_a[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 275 | IOBUF PORT "sdram_a[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 276 | IOBUF PORT "sdram_a[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 277 | IOBUF PORT "sdram_a[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 278 | IOBUF PORT "sdram_a[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 279 | IOBUF PORT "sdram_a[10]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 280 | IOBUF PORT "sdram_a[11]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 281 | IOBUF PORT "sdram_a[12]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 282 | IOBUF PORT "sdram_ba[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 283 | IOBUF PORT "sdram_ba[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 284 | IOBUF PORT "sdram_dqm[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 285 | IOBUF PORT "sdram_dqm[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 286 | IOBUF PORT "sdram_d[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 287 | IOBUF PORT "sdram_d[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 288 | IOBUF PORT "sdram_d[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 289 | IOBUF PORT "sdram_d[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 290 | IOBUF PORT "sdram_d[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 291 | IOBUF PORT "sdram_d[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 292 | IOBUF PORT "sdram_d[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 293 | IOBUF PORT "sdram_d[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 294 | IOBUF PORT "sdram_d[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 295 | IOBUF PORT "sdram_d[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 296 | IOBUF PORT "sdram_d[10]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 297 | IOBUF PORT "sdram_d[11]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 298 | IOBUF PORT "sdram_d[12]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 299 | IOBUF PORT "sdram_d[13]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 300 | IOBUF PORT "sdram_d[14]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 301 | IOBUF PORT "sdram_d[15]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; 302 | 303 | # GPDI differential interface (Video) "gpdi" sheet 304 | LOCATE COMP "gpdi_dp[0]" SITE "A16"; # Blue + 305 | LOCATE COMP "gpdi_dn[0]" SITE "B16"; # Blue - 306 | LOCATE COMP "gpdi_dp[1]" SITE "A14"; # Green + 307 | LOCATE COMP "gpdi_dn[1]" SITE "C14"; # Green - 308 | LOCATE COMP "gpdi_dp[2]" SITE "A12"; # Red + 309 | LOCATE COMP "gpdi_dn[2]" SITE "A13"; # Red - 310 | LOCATE COMP "gpdi_dp[3]" SITE "A17"; # Clock + 311 | LOCATE COMP "gpdi_dn[3]" SITE "B18"; # Clock - 312 | LOCATE COMP "gpdi_ethp" SITE "A19"; # Ethernet + 313 | LOCATE COMP "gpdi_ethn" SITE "B20"; # Ethernet - 314 | LOCATE COMP "gpdi_cec" SITE "A18"; 315 | LOCATE COMP "gpdi_sda" SITE "B19"; # I2C shared with RTC 316 | LOCATE COMP "gpdi_scl" SITE "E12"; # I2C shared with RTC C12->E12 317 | IOBUF PORT "gpdi_dp[0]" IO_TYPE=LVCMOS33D DRIVE=4; 318 | IOBUF PORT "gpdi_dn[0]" IO_TYPE=LVCMOS33D DRIVE=4; 319 | IOBUF PORT "gpdi_dp[1]" IO_TYPE=LVCMOS33D DRIVE=4; 320 | IOBUF PORT "gpdi_dn[1]" IO_TYPE=LVCMOS33D DRIVE=4; 321 | IOBUF PORT "gpdi_dp[2]" IO_TYPE=LVCMOS33D DRIVE=4; 322 | IOBUF PORT "gpdi_dn[2]" IO_TYPE=LVCMOS33D DRIVE=4; 323 | IOBUF PORT "gpdi_dp[3]" IO_TYPE=LVCMOS33D DRIVE=4; 324 | IOBUF PORT "gpdi_dn[3]" IO_TYPE=LVCMOS33D DRIVE=4; 325 | IOBUF PORT "gpdi_ethp" IO_TYPE=LVCMOS33D DRIVE=4; 326 | IOBUF PORT "gpdi_ethn" IO_TYPE=LVCMOS33D DRIVE=4; 327 | IOBUF PORT "gpdi_cec" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 328 | IOBUF PORT "gpdi_sda" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 329 | IOBUF PORT "gpdi_scl" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 330 | 331 | # GPIO (default single-ended) "gpio", "ram", "gpdi" sheet 332 | # Pins enumerated gp[0-27], gn[0-27]. 333 | # With differential mode enabled on Lattice, 334 | # gp[] (+) are used, gn[] (-) are ignored from design 335 | # as they handle inverted signal by default. 336 | # To enable differential, rename LVCMOS33->LVCMOS33D 337 | LOCATE COMP "gp[0]" SITE "B11"; # J1_5+ GP0 338 | LOCATE COMP "gn[0]" SITE "C11"; # J1_5- GN0 339 | LOCATE COMP "gp[1]" SITE "A10"; # J1_7+ GP1 340 | LOCATE COMP "gn[1]" SITE "A11"; # J1_7- GN1 341 | LOCATE COMP "gp[2]" SITE "A9"; # J1_9+ GP2 342 | LOCATE COMP "gn[2]" SITE "B10"; # J1_9- GN2 343 | LOCATE COMP "gp[3]" SITE "B9"; # J1_11+ GP3 344 | LOCATE COMP "gn[3]" SITE "C10"; # J1_11- GN3 345 | LOCATE COMP "gp[4]" SITE "A7"; # J1_13+ GP4 346 | LOCATE COMP "gn[4]" SITE "A8"; # J1_13- GN4 347 | LOCATE COMP "gp[5]" SITE "C8"; # J1_15+ GP5 348 | LOCATE COMP "gn[5]" SITE "B8"; # J1_15- GN5 349 | LOCATE COMP "gp[6]" SITE "C6"; # J1_17+ GP6 350 | LOCATE COMP "gn[6]" SITE "C7"; # J1_17- GN6 351 | IOBUF PORT "gp[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 352 | IOBUF PORT "gn[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 353 | IOBUF PORT "gp[1]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 354 | IOBUF PORT "gn[1]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 355 | IOBUF PORT "gp[2]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 356 | IOBUF PORT "gn[2]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 357 | IOBUF PORT "gp[3]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 358 | IOBUF PORT "gn[3]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 359 | IOBUF PORT "gp[4]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 360 | IOBUF PORT "gn[4]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 361 | IOBUF PORT "gp[5]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 362 | IOBUF PORT "gn[5]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 363 | IOBUF PORT "gp[6]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 364 | IOBUF PORT "gn[6]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 365 | LOCATE COMP "gp[7]" SITE "A6"; # J1_23+ GP7 366 | LOCATE COMP "gn[7]" SITE "B6"; # J1_23- GN7 367 | LOCATE COMP "gp[8]" SITE "A4"; # J1_25+ GP8 368 | LOCATE COMP "gn[8]" SITE "A5"; # J1_25- GN8 369 | LOCATE COMP "gp[9]" SITE "A2"; # J1_27+ GP9 370 | LOCATE COMP "gn[9]" SITE "B1"; # J1_27- GN9 371 | LOCATE COMP "gp[10]" SITE "C4"; # J1_29+ GP10 WIFI_GPIO27 372 | LOCATE COMP "gn[10]" SITE "B4"; # J1_29- GN10 373 | LOCATE COMP "gp[11]" SITE "F4"; # J1_31+ GP11 WIFI_GPIO25 374 | LOCATE COMP "gn[11]" SITE "E3"; # J1_31- GN11 WIFI_GPIO26 375 | LOCATE COMP "gp[12]" SITE "G3"; # J1_33+ GP12 WIFI_GPIO32 376 | LOCATE COMP "gn[12]" SITE "F3"; # J1_33- GN12 WIFI_GPIO33 377 | LOCATE COMP "gp[13]" SITE "H4"; # J1_35+ GP13 WIFI_GPIO34 378 | LOCATE COMP "gn[13]" SITE "G5"; # J1_35- GN13 WIFI_GPIO35 379 | IOBUF PORT "gp[7]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 380 | IOBUF PORT "gn[7]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 381 | IOBUF PORT "gp[8]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 382 | IOBUF PORT "gn[8]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 383 | IOBUF PORT "gp[9]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 384 | IOBUF PORT "gn[9]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 385 | IOBUF PORT "gp[10]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 386 | IOBUF PORT "gn[10]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 387 | IOBUF PORT "gp[11]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 388 | IOBUF PORT "gn[11]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 389 | IOBUF PORT "gp[12]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 390 | IOBUF PORT "gn[12]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 391 | IOBUF PORT "gp[13]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 392 | IOBUF PORT "gn[13]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 393 | LOCATE COMP "gp[14]" SITE "U18"; # J2_5+ GP14 394 | LOCATE COMP "gn[14]" SITE "U17"; # J2_5- GN14 395 | LOCATE COMP "gp[15]" SITE "N17"; # J2_7+ GP15 396 | LOCATE COMP "gn[15]" SITE "P16"; # J2_7- GN15 397 | LOCATE COMP "gp[16]" SITE "N16"; # J2_9+ GP16 398 | LOCATE COMP "gn[16]" SITE "M17"; # J2_9- GN16 399 | LOCATE COMP "gp[17]" SITE "L16"; # J2_11+ GP17 400 | LOCATE COMP "gn[17]" SITE "L17"; # J2_11- GN17 401 | LOCATE COMP "gp[18]" SITE "H18"; # J2_13+ GP18 402 | LOCATE COMP "gn[18]" SITE "H17"; # J2_13- GN18 403 | LOCATE COMP "gp[19]" SITE "F17"; # J2_15+ GP19 404 | LOCATE COMP "gn[19]" SITE "G18"; # J2_15- GN19 405 | LOCATE COMP "gp[20]" SITE "D18"; # J2_17+ GP20 406 | LOCATE COMP "gn[20]" SITE "E17"; # J2_17- GN20 407 | IOBUF PORT "gp[14]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 408 | IOBUF PORT "gn[14]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 409 | IOBUF PORT "gp[15]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 410 | IOBUF PORT "gn[15]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 411 | IOBUF PORT "gp[16]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 412 | IOBUF PORT "gn[16]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 413 | IOBUF PORT "gp[17]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 414 | IOBUF PORT "gn[17]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 415 | IOBUF PORT "gp[18]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 416 | IOBUF PORT "gn[18]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 417 | IOBUF PORT "gp[19]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 418 | IOBUF PORT "gn[19]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 419 | IOBUF PORT "gp[20]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 420 | IOBUF PORT "gn[20]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 421 | LOCATE COMP "gp[21]" SITE "C18"; # J2_23+ GP21 422 | LOCATE COMP "gn[21]" SITE "D17"; # J2_23- GN21 423 | LOCATE COMP "gp[22]" SITE "B15"; # J2_25+ GP22 D15->B15 424 | LOCATE COMP "gn[22]" SITE "C15"; # J2_25- GN22 E15->C15 425 | LOCATE COMP "gp[23]" SITE "B17"; # J2_27+ GP23 426 | LOCATE COMP "gn[23]" SITE "C17"; # J2_27- GN23 427 | LOCATE COMP "gp[24]" SITE "C16"; # J2_29+ GP24 428 | LOCATE COMP "gn[24]" SITE "D16"; # J2_29- GN24 429 | LOCATE COMP "gp[25]" SITE "D14"; # J2_31+ GP25 B15->D14 430 | LOCATE COMP "gn[25]" SITE "E14"; # J2_31- GN25 C15->E14 431 | LOCATE COMP "gp[26]" SITE "B13"; # J2_33+ GP26 432 | LOCATE COMP "gn[26]" SITE "C13"; # J2_33- GN26 433 | LOCATE COMP "gp[27]" SITE "D13"; # J2_35+ GP27 434 | LOCATE COMP "gn[27]" SITE "E13"; # J2_35- GN27 435 | IOBUF PORT "gp[21]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 436 | IOBUF PORT "gn[21]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 437 | IOBUF PORT "gp[22]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 438 | IOBUF PORT "gn[22]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 439 | IOBUF PORT "gp[23]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 440 | IOBUF PORT "gn[23]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 441 | IOBUF PORT "gp[24]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 442 | IOBUF PORT "gn[24]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 443 | IOBUF PORT "gp[25]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 444 | IOBUF PORT "gn[25]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 445 | IOBUF PORT "gp[26]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 446 | IOBUF PORT "gn[26]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 447 | IOBUF PORT "gp[27]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 448 | IOBUF PORT "gn[27]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 449 | 450 | ## PROGRAMN (reload bitstream from FLASH, exit from bootloader) 451 | # PCB v2.0.5 and higher 452 | LOCATE COMP "user_programn" SITE "M4"; 453 | IOBUF PORT "user_programn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; 454 | 455 | ## SHUTDOWN "power", "ram" sheet (connected from PCB v1.7.5) 456 | # on PCB v1.7 shutdown is not connected to FPGA 457 | LOCATE COMP "shutdown" SITE "G16"; # FPGA receives 458 | IOBUF PORT "shutdown" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; 459 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 1.4.9 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | logLevel := Level.Warn 2 | -------------------------------------------------------------------------------- /src/main/scala/HdmiTx.scala: -------------------------------------------------------------------------------- 1 | package hdmicore 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | 6 | import fpgamacro.gowin.{Oser10Module, TLVDS_OBUF} 7 | 8 | class HdmiTx() extends Module { 9 | val io = IO(new Bundle { 10 | val videoSig = Input(new VideoHdmi()) 11 | val serClk = Input(Clock()) 12 | val tmds = Output(new Tmds()) 13 | }) 14 | 15 | /* hdmi transmission */ 16 | val rgb2tmds = Module(new Rgb2Tmds()) 17 | rgb2tmds.io.videoSig <> io.videoSig 18 | 19 | /* serdes */ 20 | // Blue -> data 0 21 | val serdesBlue = Module(new Oser10Module()) 22 | serdesBlue.io.data := rgb2tmds.io.tmds_blue 23 | serdesBlue.io.fclk := io.serClk 24 | 25 | // Green -> data 1 26 | val serdesGreen = Module(new Oser10Module()) 27 | serdesGreen.io.data := rgb2tmds.io.tmds_green 28 | serdesGreen.io.fclk := io.serClk 29 | 30 | // Red -> data 2 31 | val serdesRed = Module(new Oser10Module()) 32 | serdesRed.io.data := rgb2tmds.io.tmds_red 33 | serdesRed.io.fclk := io.serClk 34 | 35 | io.tmds.data := serdesRed.io.q ## serdesGreen.io.q ## serdesBlue.io.q 36 | 37 | // clock 38 | val serdesClk = Module(new Oser10Module()) 39 | serdesClk.io.data := "b1111100000".U(10.W) 40 | serdesClk.io.fclk := io.serClk 41 | io.tmds.clk := serdesClk.io.q 42 | } 43 | -------------------------------------------------------------------------------- /src/main/scala/PatternExample.scala: -------------------------------------------------------------------------------- 1 | package hdmicore 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | 6 | import video.{VideoParams, HVSync, VideoConsts} 7 | 8 | 9 | sealed trait PatternType 10 | case object ptRainbow extends PatternType 11 | case object ptVStripes extends PatternType 12 | case object ptHStripes extends PatternType 13 | case object ptFrenchFlag extends PatternType 14 | case object ptIrishFlag extends PatternType 15 | case object ptItalianFlag extends PatternType 16 | case object ptBelgianFlag extends PatternType 17 | case object ptDutchFlag extends PatternType 18 | case object ptLuxembourgishFlag extends PatternType 19 | case object ptGermanFlag extends PatternType 20 | case object ptSpanishFlag extends PatternType 21 | case object ptAustrianFlag extends PatternType 22 | case object ptGreekFlag extends PatternType 23 | case object ptDanishFlag extends PatternType 24 | case object ptSwedishFlag extends PatternType 25 | case object ptFinnishFlag extends PatternType 26 | case object ptNorwegianFlag extends PatternType 27 | case object ptUkraineFlag extends PatternType 28 | case object ptVGradient extends PatternType 29 | case object ptHGradient extends PatternType 30 | 31 | 32 | class PatternExample(vp: VideoParams = VideoConsts.m1280x720.params, 33 | pt: PatternType = ptUkraineFlag) extends Module { 34 | val io = IO(new Bundle { 35 | val videoSig = Output(new VideoHdmi()) 36 | val I_button = Input(Bool()) 37 | }) 38 | 39 | /* todo: replace fixed values with scala convert/cast */ 40 | val ptIdxRainbow = 0.U 41 | val ptIdxVStripes = 1.U 42 | val ptIdxHStripes = 2.U 43 | val ptIdxFrenchFlag = 3.U 44 | val ptIdxIrishFlag = 4.U 45 | val ptIdxItalianFlag = 5.U 46 | val ptIdxBelgianFlag = 6.U 47 | val ptIdxDutchFlag = 7.U 48 | val ptIdxLuxembourgishFlag = 8.U 49 | val ptIdxUkraineFlag = 9.U 50 | val ptIdxSpanishFlag = 10.U 51 | val ptIdxAustrianFlag = 11.U 52 | val ptIdxGreekFlag = 12.U 53 | val ptIdxDanishFlag = 13.U 54 | val ptIdxSwedishFlag = 14.U 55 | val ptIdxFinnishFlag = 15.U 56 | val ptIdxNorwegianFlag = 16.U 57 | val ptIdxGermanFlag = 17.U 58 | val ptIdxVGradient = 18.U 59 | val ptIdxHGradient = 19.U 60 | val ptIdxBlackVoid = 30.U 61 | val ptIdxNotSet = 31.U 62 | 63 | val ptIdxDefault = pt match { 64 | case `ptRainbow` => ptIdxRainbow 65 | case `ptVStripes` => ptIdxVStripes 66 | case `ptHStripes` => ptIdxHStripes 67 | case `ptFrenchFlag` => ptIdxFrenchFlag 68 | case `ptIrishFlag` => ptIdxIrishFlag 69 | case `ptItalianFlag` => ptIdxItalianFlag 70 | case `ptBelgianFlag` => ptIdxBelgianFlag 71 | case `ptDutchFlag` => ptIdxDutchFlag 72 | case `ptLuxembourgishFlag` => ptIdxLuxembourgishFlag 73 | case `ptGermanFlag` => ptIdxGermanFlag 74 | case `ptSpanishFlag` => ptIdxSpanishFlag 75 | case `ptAustrianFlag` => ptIdxAustrianFlag 76 | case `ptGreekFlag` => ptIdxGreekFlag 77 | case `ptDanishFlag` => ptIdxDanishFlag 78 | case `ptSwedishFlag` => ptIdxSwedishFlag 79 | case `ptFinnishFlag` => ptIdxFinnishFlag 80 | case `ptNorwegianFlag` => ptIdxNorwegianFlag 81 | case `ptUkraineFlag` => ptIdxUkraineFlag 82 | case `ptVGradient` => ptIdxVGradient 83 | case `ptHGradient` => ptIdxHGradient 84 | case whoa => ptIdxBlackVoid 85 | } 86 | 87 | val hv_sync = Module(new HVSync(vp)) // Synchronize VGA module 88 | val video_de = hv_sync.io.display_on 89 | 90 | val pblue = Wire(UInt(8.W)) 91 | val pred = Wire(UInt(8.W)) 92 | val pgreen = Wire(UInt(8.W)) 93 | val hpos = hv_sync.io.hpos 94 | val vpos = hv_sync.io.vpos 95 | 96 | val ptIdx = RegInit(31.U(8.W)) 97 | val fac: Int = 100000000/100 98 | 99 | /* button debounce from https://github.com/schoeberl/chisel-book/blob/master/src/main/scala/Debounce.scala */ 100 | //- start input_func 101 | def sync(v: Bool) = RegNext(RegNext(v)) 102 | 103 | def rising(v: Bool) = v & !RegNext(v) 104 | 105 | def tickGen() = { 106 | val reg = RegInit(0.U(log2Up(fac).W)) 107 | val tick = reg === (fac-1).U 108 | reg := Mux(tick, 0.U, reg + 1.U) 109 | tick 110 | } 111 | 112 | def filter(v: Bool, t: Bool) = { 113 | val reg = RegInit(0.U(3.W)) 114 | when (t) { 115 | reg := reg(1, 0) ## v 116 | } 117 | (reg(2) & reg(1)) | (reg(2) & reg(0)) | (reg(1) & reg(0)) 118 | } 119 | 120 | def pressed (d: Bool) = { 121 | val btnSync = sync(!d) 122 | 123 | val tick = tickGen() 124 | val btnDeb = Reg(Bool()) 125 | when (tick) { 126 | btnDeb := btnSync 127 | } 128 | 129 | val btnClean = filter(btnDeb, tick) 130 | val risingEdge = rising(btnClean) 131 | risingEdge 132 | } 133 | 134 | def counter (n: UInt) = { 135 | val cntReg = RegInit(31.U(8.W)) 136 | when(pressed(io.I_button)){ 137 | cntReg := cntReg + 1.U 138 | } 139 | when(cntReg >= n) { 140 | cntReg := 0.U 141 | } 142 | when(cntReg >= ptIdxNotSet) { 143 | cntReg := ptIdxDefault 144 | } 145 | cntReg 146 | } 147 | val ptIdxNext = counter(20.U) 148 | when((hpos === 0.U && vpos === 0.U) || ptIdx === ptIdxNotSet){ 149 | ptIdx := ptIdxNext 150 | } 151 | 152 | 153 | when(!video_de){ 154 | pred := 0.U 155 | pgreen := 0.U 156 | pblue := 0.U 157 | } .elsewhen(ptIdx === ptIdxVGradient){ 158 | val x = RegNext((vpos*255.U)/vp.V_DISPLAY.U) 159 | pred := x 160 | pgreen := x 161 | pblue := 0.U 162 | } .elsewhen(ptIdx === ptIdxHGradient){ 163 | val x = RegNext((hpos*255.U)/vp.H_DISPLAY.U) 164 | pred := 0.U 165 | pgreen := 0.U 166 | pblue := x 167 | } .elsewhen(ptIdx === ptIdxRainbow){ 168 | /* generate rainbow */ 169 | /* inspired from http://blog.vermot.net/2011/11/03/generer-un-degrade-en-arc-en-ciel-en-fonction-d-une-valeur-programmatio/ */ 170 | val cTrig1 = 255.U 171 | val cTrig2 = 510.U 172 | val cTrig3 = 765.U 173 | val cTrig4 = 1020.U 174 | val cTrig5 = 1275.U 175 | val cTrig6 = 1530.U 176 | val x = RegNext((hpos*cTrig6)/vp.H_DISPLAY.U) 177 | when(x < cTrig1){ 178 | pred := cTrig1 179 | }.elsewhen(x < cTrig2) { 180 | pred := cTrig2 - x 181 | }.elsewhen(x < cTrig4){ 182 | pred := 0.U 183 | }.elsewhen(x < cTrig5){ 184 | pred := x - cTrig4 185 | }.otherwise{ 186 | pred := cTrig1 187 | } 188 | when(x < cTrig1){ 189 | pgreen := x 190 | }.elsewhen(x < cTrig3){ 191 | pgreen := cTrig1 192 | }.elsewhen(x < cTrig4){ 193 | pgreen := cTrig4 - x 194 | }.otherwise{ 195 | pgreen := 0.U 196 | } 197 | 198 | when(x < cTrig2){ 199 | pblue := 0.U 200 | }.elsewhen(x < cTrig3){ 201 | pblue := x - cTrig2 202 | }.elsewhen(x < cTrig5){ 203 | pblue := cTrig1 204 | }.elsewhen(x < cTrig6){ 205 | pblue := cTrig6 - x 206 | }.otherwise { 207 | pblue := 0.U 208 | } 209 | } .elsewhen(ptIdx === ptIdxVStripes){ 210 | pred := Mux(0.U === hpos % 2.U, 0.U, 255.U) 211 | pgreen := Mux(0.U === hpos % 2.U, 0.U, 255.U) 212 | pblue := Mux(0.U === hpos % 2.U, 0.U, 255.U) 213 | } .elsewhen(ptIdx === ptIdxHStripes){ 214 | pred := Mux(0.U === vpos % 2.U, 0.U, 255.U) 215 | pgreen := Mux(0.U === vpos % 2.U, 0.U, 255.U) 216 | pblue := Mux(0.U === vpos % 2.U, 0.U, 255.U) 217 | } .elsewhen(ptIdx === ptIdxFrenchFlag){ 218 | val swidth = vp.H_DISPLAY 219 | pred := Mux(hpos < (swidth/3).U, 0.U, 255.U) 220 | pgreen := Mux((hpos >= (swidth/3).U) && (hpos < (swidth*2/3).U), 255.U, 0.U) 221 | pblue := Mux(hpos < (swidth*2/3).U, 255.U, 0.U) 222 | } .elsewhen(ptIdx === ptIdxIrishFlag){ 223 | val swidth = vp.H_DISPLAY 224 | pred := Mux(hpos < (swidth/3).U, 0.U, 255.U) 225 | pgreen := Mux(hpos < (swidth*2/3).U, 255.U, 128.U) 226 | pblue := Mux((hpos >= (swidth/3).U) && (hpos < (swidth*2/3).U), 255.U, 0.U) 227 | } .elsewhen(ptIdx === ptIdxItalianFlag){ 228 | val swidth = vp.H_DISPLAY 229 | pred := Mux(hpos < (swidth/3).U, 0.U, 255.U) 230 | pgreen := Mux(hpos < (swidth*2/3).U, 255.U, 0.U) 231 | pblue := Mux((hpos >= (swidth/3).U) && (hpos < (swidth*2/3).U), 255.U, 0.U) 232 | } .elsewhen(ptIdx === ptIdxBelgianFlag){ 233 | val swidth = vp.H_DISPLAY 234 | pred := Mux(hpos >= (swidth/3).U, 255.U, 0.U) 235 | pgreen := Mux((hpos >= (swidth/3).U) && (hpos < (swidth*2/3).U), 255.U, 0.U) 236 | pblue := 0.U 237 | } .elsewhen(ptIdx === ptIdxDutchFlag){ 238 | val sheight = vp.V_DISPLAY 239 | val prbright = Mux(vpos < (sheight/3).U, 128.U, 255.U) 240 | val pbbright = Mux(vpos < (sheight*2/3).U, 255.U, 128.U) 241 | pred := Mux(vpos < (sheight*2/3).U, prbright, 0.U) 242 | pgreen := Mux((vpos >= (sheight/3).U) && (vpos < (sheight*2/3).U), 255.U, 0.U) 243 | pblue := Mux(vpos < (sheight/3).U, 0.U, pbbright) 244 | } .elsewhen(ptIdx === ptIdxLuxembourgishFlag){ 245 | val sheight = vp.V_DISPLAY 246 | pred := Mux(vpos < (sheight*2/3).U, 255.U, 0.U) 247 | pgreen := Mux((vpos >= (sheight/3).U) && (vpos < (sheight*2/3).U), 255.U, 0.U) 248 | pblue := Mux(vpos < (sheight/3).U, 0.U, 255.U) 249 | } .elsewhen(ptIdx === ptIdxGermanFlag){ 250 | val sheight = vp.V_DISPLAY 251 | pred := Mux(vpos >= (sheight/3).U, 255.U, 0.U) 252 | pgreen := Mux(vpos < (sheight*2/3).U, 0.U, 255.U) 253 | pblue := 0.U 254 | } .elsewhen(ptIdx === ptIdxSpanishFlag){ 255 | val sheight = vp.V_DISPLAY 256 | pred := 255.U 257 | pgreen := Mux((vpos >= (sheight/4).U) && (vpos < (sheight*3/4).U), 255.U, 0.U) 258 | pblue := 0.U 259 | } .elsewhen(ptIdx === ptIdxAustrianFlag){ 260 | val sheight = vp.V_DISPLAY 261 | pred := 255.U 262 | pgreen := Mux((vpos >= (sheight/3).U) && (vpos < (sheight*2/3).U), 255.U, 0.U) 263 | pblue := Mux((vpos >= (sheight/3).U) && (vpos < (sheight*2/3).U), 255.U, 0.U) 264 | } .elsewhen(ptIdx === ptIdxGreekFlag){ 265 | val swidth = vp.H_DISPLAY 266 | val sheight = vp.V_DISPLAY 267 | val swstep = swidth*3/80 268 | val shstep = sheight/9 269 | val oinv = Mux((hpos <= (swstep*10).U) && (vpos > (shstep*2).U) && (vpos <= (shstep*3).U), 255.U, 0.U) 270 | val pinv = Mux((hpos > (swstep*4).U) && (hpos <= (swstep*6).U) && (vpos <= (shstep*5).U), 255.U, oinv) 271 | val ninv = Mux(((hpos <= (swstep*4).U) || (hpos > (swstep*6).U)) && (hpos <= (swstep*10).U) && (vpos <= (shstep*5).U), 0.U, 255.U) 272 | pred := Mux(vpos % (sheight*2/9).U > (sheight/9).U, ninv, pinv) 273 | pgreen := Mux(vpos % (sheight*2/9).U > (sheight/9).U, ninv, pinv) 274 | pblue := 255.U 275 | } .elsewhen(ptIdx === ptIdxDanishFlag){ 276 | val swidth = vp.H_DISPLAY 277 | val sheight = vp.V_DISPLAY 278 | val swstep = swidth*4/37 279 | val shstep = sheight/7 280 | val pinv = Mux((hpos > (swstep*3).U) && (hpos <= (swstep*4).U), 255.U, 0.U) 281 | pred := 255.U 282 | pgreen := Mux((vpos > (shstep*3).U) && (vpos <= (shstep*4).U), 255.U, pinv) 283 | pblue := Mux((vpos > (shstep*3).U) && (vpos <= (shstep*4).U), 255.U, pinv) 284 | } .elsewhen(ptIdx === ptIdxSwedishFlag){ 285 | val swidth = vp.H_DISPLAY 286 | val sheight = vp.V_DISPLAY 287 | val swstep = swidth/16 288 | val shstep = sheight/5 289 | val pinv = Mux((hpos > (swstep*5).U) && (hpos <= (swstep*7).U), 255.U, 0.U) 290 | val ninv = Mux((hpos > (swstep*5).U) && (hpos <= (swstep*7).U), 0.U, 255.U) 291 | pred := Mux((vpos > (shstep*2).U) && (vpos <= (shstep*3).U), 255.U, pinv) 292 | pgreen := Mux((vpos > (shstep*2).U) && (vpos <= (shstep*3).U), 255.U, pinv) 293 | pblue := Mux((vpos > (shstep*2).U) && (vpos <= (shstep*3).U), 0.U, ninv) 294 | } .elsewhen(ptIdx === ptIdxFinnishFlag){ 295 | val swidth = vp.H_DISPLAY 296 | val sheight = vp.V_DISPLAY 297 | val swstep = swidth/18 298 | val shstep = sheight/11 299 | val ninv = Mux((hpos > (swstep*5).U) && (hpos <= (swstep*8).U), 0.U, 255.U) 300 | pred := Mux((vpos > (shstep*4).U) && (vpos <= (shstep*7).U), 0.U, ninv) 301 | pgreen := Mux((vpos > (shstep*4).U) && (vpos <= (shstep*7).U), 0.U, ninv) 302 | pblue := 255.U 303 | } .elsewhen(ptIdx === ptIdxNorwegianFlag){ 304 | val swidth = vp.H_DISPLAY 305 | val sheight = vp.V_DISPLAY 306 | val swstep = swidth/22 307 | val shstep = sheight/16 308 | val minv = Mux((vpos > (shstep*7).U) && (vpos <= (shstep*9).U), 0.U, 255.U) 309 | val linv = Mux(((hpos > (swstep*6).U) && (hpos <= (swstep*7).U)) || ((hpos > (swstep*9).U) && (hpos <= (swstep*10).U)), minv, 0.U) 310 | val kinv = Mux((hpos > (swstep*7).U) && (hpos <= (swstep*9).U), 0.U, 255.U) 311 | val pgbright = Mux(((vpos > (shstep*6).U) && (vpos <= (shstep*7).U)) || ((vpos > (shstep*9).U) && (vpos <= (shstep*10).U)), kinv, linv) 312 | val prbright = Mux(pgbright > 0.U, 255.U, 128.U) 313 | val pbbright = Mux(pgbright > 0.U, 255.U, 128.U) 314 | val pinv = Mux((hpos > (swstep*6).U) && (hpos <= (swstep*10).U), pbbright, 0.U) 315 | val ninv = Mux((hpos > (swstep*7).U) && (hpos <= (swstep*9).U), 0.U, prbright) 316 | pred := Mux((vpos > (shstep*7).U) && (vpos <= (shstep*9).U), 0.U, ninv) 317 | pgreen := Mux(((vpos > (shstep*6).U) && (vpos <= (shstep*7).U)) || ((vpos > (shstep*9).U) && (vpos <= (shstep*10).U)), kinv, linv) 318 | pblue := Mux((vpos > (shstep*6).U) && (vpos <= (shstep*10).U), pbbright, pinv) 319 | } .elsewhen(ptIdx === ptIdxUkraineFlag){ 320 | /* blue #00 57 b7, yellow #ff d7 00 */ 321 | val sheight = vp.V_DISPLAY 322 | pred := Mux(vpos <= (sheight/2).U, "h00".U, "hFF".U) 323 | pgreen:= Mux(vpos <= (sheight/2).U, "h00".U, "hFF".U) 324 | pblue := Mux(vpos <= (sheight/2).U, "hFF".U, "h00".U) 325 | } .otherwise { 326 | pred := 0.U 327 | pgreen := 0.U 328 | pblue := 0.U 329 | } 330 | 331 | io.videoSig.de := video_de 332 | io.videoSig.hsync := hv_sync.io.hsync 333 | io.videoSig.vsync := hv_sync.io.vsync 334 | io.videoSig.pixel.red := pred 335 | io.videoSig.pixel.green := pgreen 336 | io.videoSig.pixel.blue := pblue 337 | } 338 | -------------------------------------------------------------------------------- /src/main/scala/Rgb2Tmds.scala: -------------------------------------------------------------------------------- 1 | package hdmicore 2 | 3 | import chisel3._ 4 | import circt.stage.ChiselStage 5 | import chisel3.util._ 6 | 7 | class Rgb2Tmds extends Module { 8 | val io = IO(new Bundle { 9 | val videoSig = Input(new VideoHdmi()) 10 | val tmds_blue = Output(UInt(10.W)) 11 | val tmds_red = Output(UInt(10.W)) 12 | val tmds_green = Output(UInt(10.W)) 13 | }) 14 | 15 | /* Blue and controls */ 16 | val tbM = Module(new TMDSEncoder()) 17 | tbM.io.en := io.videoSig.de 18 | tbM.io.ctrl := io.videoSig.vsync ## io.videoSig.hsync 19 | tbM.io.din := io.videoSig.pixel.blue 20 | io.tmds_blue := tbM.io.dout 21 | 22 | /* red */ 23 | val trM = Module(new TMDSEncoder()) 24 | trM.io.en := io.videoSig.de 25 | trM.io.ctrl := 0.U 26 | trM.io.din := io.videoSig.pixel.red 27 | io.tmds_red := trM.io.dout 28 | 29 | /* green */ 30 | val tgM = Module(new TMDSEncoder()) 31 | tgM.io.en := io.videoSig.de 32 | tgM.io.ctrl := 0.U 33 | tgM.io.din := io.videoSig.pixel.green 34 | io.tmds_green := tgM.io.dout 35 | } 36 | 37 | object Rgb2Tmds extends App { 38 | val verilog_src = ChiselStage 39 | .emitSystemVerilog(new Rgb2Tmds, 40 | firtoolOpts = Array( 41 | "-disable-all-randomization", 42 | "--lowering-options=disallowLocalVariables", // avoid 'automatic logic' 43 | "-strip-debug-info")) 44 | val fverilog = os.pwd / "Rgb2Tmds.v" 45 | if(os.exists(fverilog)) 46 | os.remove(fverilog) 47 | os.write(fverilog, verilog_src) 48 | } 49 | -------------------------------------------------------------------------------- /src/main/scala/TmdsEncoder.scala: -------------------------------------------------------------------------------- 1 | package hdmicore 2 | 3 | import chisel3._ 4 | import circt.stage.ChiselStage 5 | import chisel3.util._ 6 | 7 | class TMDSEncoder extends Module { 8 | val io = IO(new Bundle { 9 | val en = Input(Bool()) 10 | val ctrl = Input(UInt(2.W)) 11 | val din = Input(UInt(8.W)) 12 | val dout = Output(UInt(10.W)) 13 | }) 14 | 15 | /* ones counter for input data */ 16 | val n_one_din = PopCount(io.din) 17 | /* create xor encodings */ 18 | def xorfct(value: UInt): UInt = { 19 | val vin = VecInit(value.asBools) 20 | val res = VecInit(511.U.asBools) 21 | res(0) := vin(0) 22 | for(i <- 1 to 7){ 23 | res(i) := res(i-1) ^ vin(i) 24 | } 25 | res(8) := 1.U 26 | res.asUInt 27 | } 28 | val xored = xorfct(io.din) 29 | 30 | /* create xnor encodings */ 31 | def xnorfct(value: UInt): UInt = { 32 | val vin = VecInit(value.asBools) 33 | val res = VecInit(511.U.asBools) 34 | res(0) := vin(0) 35 | for(i <- 1 to 7){ 36 | res(i) := !(res(i-1) ^ vin(i)) 37 | } 38 | res(8) := 0.U 39 | res.asUInt 40 | } 41 | val xnored = xnorfct(io.din) 42 | 43 | /* use xnored or xored data based on the ones */ 44 | val q_m = RegInit(0.U(9.W)) 45 | q_m := Mux( 46 | (n_one_din > 4.U) || (n_one_din === 4.U && io.din(0) === 0.U), 47 | xnored, xored) 48 | 49 | /* ones counter for internal data */ 50 | val diffSize = 4 51 | val diff = RegInit(0.S(diffSize.W)) 52 | diff := PopCount(q_m).asSInt - 4.S 53 | 54 | val disparitySize = 4 55 | val disparityReg = RegInit(0.S(disparitySize.W)) 56 | val doutReg = RegInit("b1010101011".U(10.W)) 57 | when(io.en === false.B){ 58 | disparityReg := 0.S 59 | doutReg := "b1010101011".U(10.W) 60 | switch(io.ctrl){ 61 | is("b00".U(2.W)){doutReg := "b1101010100".U(10.W)} 62 | is("b01".U(2.W)){doutReg := "b0010101011".U(10.W)} 63 | is("b10".U(2.W)){doutReg := "b0101010100".U(10.W)} 64 | } 65 | }.otherwise{ 66 | when(disparityReg === 0.S || diff === 0.S){ 67 | /* xnored data */ 68 | when(q_m(8) === false.B){ 69 | doutReg := "b10".U(2.W) ## ~q_m(7, 0) 70 | disparityReg := disparityReg - diff 71 | }.otherwise{ 72 | doutReg := "b01".U(2.W) ## q_m(7, 0) 73 | disparityReg := disparityReg + diff 74 | } 75 | }.elsewhen( (!diff(diffSize-1) && !disparityReg(disparitySize - 1)) 76 | || (diff(diffSize-1) && disparityReg(disparitySize - 1))){ 77 | doutReg := 1.U(1.W) ## q_m(8) ## ~q_m(7, 0) 78 | when(q_m(8)){ 79 | disparityReg := disparityReg + 1.S - diff 80 | }.otherwise{ 81 | disparityReg := disparityReg - diff 82 | } 83 | }.otherwise{ 84 | doutReg := 0.U(1.W) ## q_m 85 | when(q_m(8)){ 86 | disparityReg := disparityReg + diff 87 | }.otherwise{ 88 | disparityReg := disparityReg - 1.S + diff 89 | } 90 | } 91 | } 92 | io.dout := doutReg 93 | } 94 | 95 | object TMDSEncoder extends App { 96 | val verilog_src = ChiselStage 97 | .emitSystemVerilog( 98 | new TMDSEncoder, 99 | firtoolOpts = Array( 100 | "-disable-all-randomization", 101 | "--lowering-options=disallowLocalVariables", // avoid 'automatic logic' 102 | "-strip-debug-info")) 103 | val fverilog = os.pwd / "TMDSEncoder.v" 104 | if(os.exists(fverilog)) 105 | os.remove(fverilog) 106 | os.write(fverilog, verilog_src) 107 | } 108 | -------------------------------------------------------------------------------- /src/main/scala/TmdsIncludes.scala: -------------------------------------------------------------------------------- 1 | package hdmicore 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | 6 | class RGBColors extends Bundle { 7 | val red = UInt(8.W) 8 | val green = UInt(8.W) 9 | val blue = UInt(8.W) 10 | } 11 | 12 | class VideoHdmi extends Bundle { 13 | val pixel = new RGBColors() 14 | val de = Bool() 15 | val hsync = Bool() 16 | val vsync = Bool() 17 | } 18 | 19 | class Tmds extends Bundle { 20 | val clk = Bool() 21 | val data = UInt(3.W) 22 | } 23 | 24 | class DiffPair extends Bundle { 25 | val p = Bool() 26 | val n = Bool() 27 | } 28 | 29 | class TMDSDiff extends Bundle { 30 | val clk = new DiffPair() 31 | val data = Vec(3, new DiffPair()) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/main/scala/platforms/tangnano4k.scala: -------------------------------------------------------------------------------- 1 | package hdmicore.platforms 2 | 3 | /** Pattern generation HDMI example for TangNano4k 4 | */ 5 | 6 | import chisel3._ 7 | import circt.stage.ChiselStage 8 | import chisel3.util._ 9 | 10 | import fpgamacro.gowin.{CLKDIV, TMDS_PLLVR, TLVDS_OBUF} 11 | import hdmicore.{PatternExample, TMDSDiff, DiffPair, HdmiTx} 12 | import hdmicore.video.{VideoMode, VideoConsts} 13 | 14 | class TangNano4k(vmode: VideoMode = VideoConsts.m1280x720) extends RawModule { 15 | 16 | /************/ 17 | /** outputs */ 18 | /* Clock and reset */ 19 | val I_clk = IO(Input(Clock())) 20 | val I_reset_n = IO(Input(Bool())) 21 | 22 | /* Debug leds */ 23 | val O_led = IO(Output(UInt(2.W))) 24 | 25 | /* TMDS (HDMI) signals */ 26 | val O_tmds = IO(Output(new TMDSDiff())) 27 | 28 | /* button */ 29 | val I_button = IO(Input(Bool())) 30 | 31 | /********************************************/ 32 | 33 | O_led := 1.U(2.W) 34 | 35 | val pll_lock = Wire(Bool()) 36 | val serial_clk = Wire(Clock()) 37 | val pix_clk = Wire(Clock()) 38 | 39 | val glb_rst = ~(pll_lock & I_reset_n) 40 | 41 | /* CLKDIV */ 42 | val clkDiv = Module(new CLKDIV()) 43 | clkDiv.io.RESETN := ~glb_rst 44 | clkDiv.io.HCLKIN := serial_clk 45 | pix_clk := clkDiv.io.CLKOUT 46 | clkDiv.io.CALIB := true.B 47 | 48 | /* TMDS PLL */ 49 | val tmdsPllvr = Module(new TMDS_PLLVR(vmode.pll)) 50 | tmdsPllvr.io.clkin := I_clk 51 | serial_clk := tmdsPllvr.io.clkout 52 | pll_lock := tmdsPllvr.io.lock 53 | 54 | withClockAndReset(pix_clk, glb_rst) { 55 | 56 | /* counter debug */ 57 | val max_count = 27000000 58 | val (counterReg, counterPulse) = Counter(true.B, max_count) 59 | O_led := (counterReg >= (max_count/2).U) 60 | 61 | val patternExample = Module(new PatternExample(vmode.params)) 62 | patternExample.io.I_button := I_button 63 | 64 | val hdmiTx = Module(new HdmiTx()) 65 | hdmiTx.io.serClk := serial_clk 66 | patternExample.io.videoSig <> hdmiTx.io.videoSig 67 | 68 | /* LVDS output */ 69 | val buffDiffBlue = Module(new TLVDS_OBUF()) 70 | buffDiffBlue.io.I := hdmiTx.io.tmds.data(0) 71 | val buffDiffGreen = Module(new TLVDS_OBUF()) 72 | buffDiffGreen.io.I := hdmiTx.io.tmds.data(1) 73 | val buffDiffRed = Module(new TLVDS_OBUF()) 74 | buffDiffRed.io.I := hdmiTx.io.tmds.data(2) 75 | val buffDiffClk = Module(new TLVDS_OBUF()) 76 | buffDiffClk.io.I := hdmiTx.io.tmds.clk 77 | 78 | O_tmds.data(0).p := buffDiffBlue.io.O 79 | O_tmds.data(0).n := buffDiffBlue.io.OB 80 | O_tmds.data(1).p := buffDiffGreen.io.O 81 | O_tmds.data(1).n := buffDiffGreen.io.OB 82 | O_tmds.data(2).p := buffDiffRed.io.O 83 | O_tmds.data(2).n := buffDiffRed.io.OB 84 | O_tmds.clk.p := buffDiffClk.io.O 85 | O_tmds.clk.n := buffDiffClk.io.OB 86 | } 87 | } 88 | 89 | object TangNano4k extends App { 90 | val verilog_src = ChiselStage 91 | .emitSystemVerilog( 92 | new TangNano4k, 93 | firtoolOpts = Array( 94 | "-disable-all-randomization", 95 | "--lowering-options=disallowLocalVariables", // avoid 'automatic logic' 96 | "-strip-debug-info")) 97 | val fverilog = os.pwd / "TangNano4k.v" 98 | if(os.exists(fverilog)) 99 | os.remove(fverilog) 100 | os.write(fverilog, verilog_src) 101 | } 102 | -------------------------------------------------------------------------------- /src/main/scala/platforms/tangnano9k.scala: -------------------------------------------------------------------------------- 1 | package hdmicore.platforms 2 | 3 | /** Pattern generation HDMI example for TangNano9k 4 | */ 5 | 6 | import chisel3._ 7 | import circt.stage.ChiselStage 8 | import chisel3.util._ 9 | 10 | import fpgamacro.gowin.{CLKDIV, Gowin_rPLL, ELVDS_OBUF} 11 | import hdmicore.{PatternExample, TMDSDiff, DiffPair, HdmiTx} 12 | import hdmicore.video.{VideoMode, VideoConsts} 13 | 14 | class TangNano9k(vmode: VideoMode = VideoConsts.m1280x720) extends RawModule { 15 | 16 | /************/ 17 | /** outputs */ 18 | /* Clock and reset */ 19 | val I_clk = IO(Input(Clock())) 20 | val I_reset_n = IO(Input(Bool())) 21 | 22 | /* Debug leds */ 23 | val O_led = IO(Output(UInt(2.W))) 24 | 25 | /* TMDS (HDMI) signals */ 26 | val O_tmds = IO(Output(new TMDSDiff())) 27 | 28 | /* button */ 29 | val I_button = IO(Input(Bool())) 30 | 31 | /********************************************/ 32 | 33 | O_led := 1.U(2.W) 34 | 35 | val pll_lock = Wire(Bool()) 36 | val serial_clk = Wire(Clock()) 37 | val pix_clk = Wire(Clock()) 38 | 39 | val glb_rst = ~(pll_lock & I_reset_n) 40 | 41 | /* CLKDIV */ 42 | val clkDiv = Module(new CLKDIV()) 43 | clkDiv.io.RESETN := ~glb_rst 44 | clkDiv.io.HCLKIN := serial_clk 45 | pix_clk := clkDiv.io.CLKOUT 46 | clkDiv.io.CALIB := true.B 47 | 48 | /* TMDS PLL */ 49 | val tmdsPllvr = Module(new Gowin_rPLL(vmode.pll)) 50 | tmdsPllvr.io.clkin := I_clk 51 | serial_clk := tmdsPllvr.io.clkout 52 | pll_lock := tmdsPllvr.io.lock 53 | 54 | withClockAndReset(pix_clk, glb_rst) { 55 | 56 | /* counter debug */ 57 | val max_count = 27000000 58 | val (counterReg, counterPulse) = Counter(true.B, max_count) 59 | O_led := (counterReg >= (max_count/2).U) 60 | 61 | val hdmiTx = Module(new HdmiTx()) 62 | hdmiTx.io.serClk := serial_clk 63 | val patternExample = Module(new PatternExample(vmode.params)) 64 | hdmiTx.io.videoSig := patternExample.io.videoSig 65 | patternExample.io.I_button := I_button 66 | 67 | 68 | /* LVDS output */ 69 | val buffDiffBlue = Module(new ELVDS_OBUF()) 70 | buffDiffBlue.io.I := hdmiTx.io.tmds.data(0) 71 | val buffDiffGreen = Module(new ELVDS_OBUF()) 72 | buffDiffGreen.io.I := hdmiTx.io.tmds.data(1) 73 | val buffDiffRed = Module(new ELVDS_OBUF()) 74 | buffDiffRed.io.I := hdmiTx.io.tmds.data(2) 75 | val buffDiffClk = Module(new ELVDS_OBUF()) 76 | buffDiffClk.io.I := hdmiTx.io.tmds.clk 77 | 78 | O_tmds.data(0).p := buffDiffBlue.io.O 79 | O_tmds.data(0).n := buffDiffBlue.io.OB 80 | O_tmds.data(1).p := buffDiffGreen.io.O 81 | O_tmds.data(1).n := buffDiffGreen.io.OB 82 | O_tmds.data(2).p := buffDiffRed.io.O 83 | O_tmds.data(2).n := buffDiffRed.io.OB 84 | O_tmds.clk.p := buffDiffClk.io.O 85 | O_tmds.clk.n := buffDiffClk.io.OB 86 | } 87 | } 88 | 89 | object TangNano9k extends App { 90 | val verilog_src = ChiselStage 91 | .emitSystemVerilog( 92 | new TangNano9k, 93 | firtoolOpts = Array( 94 | "-disable-all-randomization", 95 | "--lowering-options=disallowLocalVariables", // avoid 'automatic logic' 96 | "-strip-debug-info")) 97 | val fverilog = os.pwd / "TangNano9k.v" 98 | if(os.exists(fverilog)) 99 | os.remove(fverilog) 100 | os.write(fverilog, verilog_src) 101 | } 102 | -------------------------------------------------------------------------------- /src/main/scala/platforms/ulx3s.scala: -------------------------------------------------------------------------------- 1 | package hdmicore.platforms 2 | 3 | /** Pattern generation HDMI example for Ulx3s 4 | */ 5 | 6 | import chisel3._ 7 | import circt.stage.ChiselStage 8 | import chisel3.util._ 9 | 10 | import fpgamacro.gowin.{CLKDIV, TMDS_PLLVR, TLVDS_OBUF} 11 | import hdmicore.{PatternExample, TMDSDiff, DiffPair, HdmiTx} 12 | 13 | class Ulx3s extends RawModule { 14 | 15 | /************/ 16 | /** outputs */ 17 | /* Clock and reset */ 18 | val I_clk = IO(Input(Clock())) 19 | val I_reset_n = IO(Input(Bool())) 20 | 21 | /* Debug leds */ 22 | val O_led = IO(Output(UInt(2.W))) 23 | 24 | /* TMDS (HDMI) signals */ 25 | val O_tmds = IO(Output(new TMDSDiff())) 26 | 27 | /* button */ 28 | val I_button = IO(Input(Bool())) 29 | 30 | /********************************************/ 31 | 32 | O_led := 1.U(2.W) 33 | 34 | val pll_lock = Wire(Bool()) 35 | val serial_clk = Wire(Clock()) 36 | val pix_clk = Wire(Clock()) 37 | 38 | val glb_rst = ~(pll_lock & I_reset_n) 39 | 40 | /* CLKDIV */ 41 | val clkDiv = Module(new CLKDIV()) 42 | clkDiv.io.RESETN := ~glb_rst 43 | clkDiv.io.HCLKIN := serial_clk 44 | pix_clk := clkDiv.io.CLKOUT 45 | clkDiv.io.CALIB := true.B 46 | 47 | /* TMDS PLL */ 48 | val tmdsPllvr = Module(new TMDS_PLLVR()) 49 | tmdsPllvr.io.clkin := I_clk 50 | serial_clk := tmdsPllvr.io.clkout 51 | pll_lock := tmdsPllvr.io.lock 52 | 53 | withClockAndReset(pix_clk, glb_rst) { 54 | 55 | /* counter debug */ 56 | val max_count = 27000000 57 | val (counterReg, counterPulse) = Counter(true.B, max_count) 58 | O_led := (counterReg >= (max_count/2).U) 59 | 60 | val patternExample = Module(new PatternExample()) 61 | patternExample.io.I_button := I_button 62 | 63 | val hdmiTx = Module(new HdmiTx()) 64 | hdmiTx.io.serClk := serial_clk 65 | patternExample.io.videoSig <> hdmiTx.io.videoSig 66 | 67 | /* LVDS output */ 68 | val buffDiffBlue = Module(new TLVDS_OBUF()) 69 | buffDiffBlue.io.I := hdmiTx.io.tmds.data(0) 70 | val buffDiffGreen = Module(new TLVDS_OBUF()) 71 | buffDiffGreen.io.I := hdmiTx.io.tmds.data(1) 72 | val buffDiffRed = Module(new TLVDS_OBUF()) 73 | buffDiffRed.io.I := hdmiTx.io.tmds.data(2) 74 | val buffDiffClk = Module(new TLVDS_OBUF()) 75 | buffDiffClk.io.I := hdmiTx.io.tmds.clk 76 | 77 | O_tmds.data(0).p := buffDiffBlue.io.O 78 | O_tmds.data(0).n := buffDiffBlue.io.OB 79 | O_tmds.data(1).p := buffDiffGreen.io.O 80 | O_tmds.data(1).n := buffDiffGreen.io.OB 81 | O_tmds.data(2).p := buffDiffRed.io.O 82 | O_tmds.data(2).n := buffDiffRed.io.OB 83 | O_tmds.clk.p := buffDiffClk.io.O 84 | O_tmds.clk.n := buffDiffClk.io.OB 85 | } 86 | } 87 | 88 | object Ulx3s extends App { 89 | val verilog_src = ChiselStage 90 | .emitSystemVerilog( 91 | new Ulx3s, 92 | firtoolOpts = Array( 93 | "-disable-all-randomization", 94 | "--lowering-options=disallowLocalVariables", // avoid 'automatic logic' 95 | "-strip-debug-info")) 96 | val fverilog = os.pwd / "Ulx3s.v" 97 | if(os.exists(fverilog)) 98 | os.remove(fverilog) 99 | os.write(fverilog, verilog_src) 100 | } 101 | -------------------------------------------------------------------------------- /src/main/scala/video/VideoConsts.scala: -------------------------------------------------------------------------------- 1 | package hdmicore.video 2 | 3 | import chisel3._ 4 | import fpgamacro.gowin.PLLParams 5 | 6 | case class VideoMode( 7 | val params: VideoParams, 8 | val pll: PLLParams 9 | ) 10 | 11 | package object VideoConsts { 12 | val p25920khz = PLLParams(IDIV_SEL = 4, FBDIV_SEL = 23, ODIV_SEL = 8, DYN_SDIV_SEL = 4) // DYN_SDIV_SEL = 10 13 | val p27000khz = PLLParams(IDIV_SEL = 0, FBDIV_SEL = 4, ODIV_SEL = 8, DYN_SDIV_SEL = 10) 14 | val p40000khz = PLLParams(IDIV_SEL = 4, FBDIV_SEL = 36, ODIV_SEL = 4, DYN_SDIV_SEL = 16) 15 | val p51400khz = PLLParams(IDIV_SEL = 1, FBDIV_SEL = 18, ODIV_SEL = 4, DYN_SDIV_SEL = 20) 16 | val p65000khz = PLLParams(IDIV_SEL = 0, FBDIV_SEL = 11, ODIV_SEL = 2, DYN_SDIV_SEL = 26) 17 | val p71100khz = PLLParams(IDIV_SEL = 3, FBDIV_SEL = 52, ODIV_SEL = 2, DYN_SDIV_SEL = 30) 18 | val p74250khz = PLLParams(IDIV_SEL = 3, FBDIV_SEL = 54, ODIV_SEL = 2, DYN_SDIV_SEL = 30) 19 | val p85500khz = PLLParams(IDIV_SEL = 3, FBDIV_SEL = 62, ODIV_SEL = 2, DYN_SDIV_SEL = 34) 20 | val p106500khz = PLLParams(IDIV_SEL = 2, FBDIV_SEL = 58, ODIV_SEL = 2, DYN_SDIV_SEL = 42) 21 | val p108000khz = PLLParams(IDIV_SEL = 0, FBDIV_SEL = 19, ODIV_SEL = 2, DYN_SDIV_SEL = 44) 22 | 23 | // to be checked 24 | // D: 27.00 MHz, H: 31.469 kHz, V: 59.94 Hz 25 | val m720x480 = VideoMode( 26 | params = VideoParams( 27 | H_DISPLAY = 720, H_FRONT = 16, 28 | H_SYNC = 62, H_BACK = 60, 29 | V_SYNC = 6, V_BACK = 30, 30 | V_TOP = 9, V_DISPLAY = 480, 31 | V_BOTTOM = 30 32 | ), 33 | pll = p27000khz 34 | ) 35 | 36 | // D: 27.00 MHz, H: 31.250 kHz, V: 50.00 Hz 37 | val m720x576 = VideoMode( 38 | params = VideoParams( 39 | H_DISPLAY = 720, H_FRONT = 12, 40 | H_SYNC = 64, H_BACK = 68, 41 | V_SYNC = 5, V_BACK = 39, 42 | V_TOP = 5, V_DISPLAY = 576, 43 | V_BOTTOM = 39 44 | ), 45 | pll = p27000khz 46 | ) 47 | 48 | // D: 25,92 MHz, only for supported displays 49 | val m800x480 = VideoMode( 50 | params = VideoParams( 51 | H_DISPLAY = 800, H_FRONT = 210, 52 | H_SYNC = 1, H_BACK = 182, 53 | V_SYNC = 5, V_BACK = 0, 54 | V_TOP = 45, V_DISPLAY = 480, 55 | V_BOTTOM = 0 56 | ), 57 | pll = p25920khz 58 | ) 59 | 60 | // to be checked 61 | // D: 40.00 MHz, H: 37.879 kHz, V: 60.32 Hz 62 | val m800x600 = VideoMode( 63 | params = VideoParams( 64 | H_DISPLAY = 800, H_FRONT = 40, 65 | H_SYNC = 128, H_BACK = 88, 66 | V_SYNC = 4, V_BACK = 23, 67 | V_TOP = 1, V_DISPLAY = 600, 68 | V_BOTTOM = 23 69 | ), 70 | pll = p40000khz 71 | ) 72 | 73 | // D: 51.40 MHz, H: 38.280 kHz, V: 60.00 Hz 74 | val m1024x600 = VideoMode( 75 | params = VideoParams( 76 | H_DISPLAY = 1024, H_FRONT = 24, 77 | H_SYNC = 136, H_BACK = 160, 78 | V_SYNC = 6, V_BACK = 29, 79 | V_TOP = 3, V_DISPLAY = 600, 80 | V_BOTTOM = 29 81 | ), 82 | pll = p51400khz 83 | ) 84 | 85 | // D: 65.00 MHz, H: 48.363 kHz, V: 60.00 Hz 86 | val m1024x768 = VideoMode( 87 | params = VideoParams( 88 | H_DISPLAY = 1024, H_FRONT = 24, 89 | H_SYNC = 136, H_BACK = 160, 90 | V_SYNC = 6, V_BACK = 29, 91 | V_TOP = 3, V_DISPLAY = 768, 92 | V_BOTTOM = 29 93 | ), 94 | pll = p65000khz 95 | ) 96 | 97 | // D: 74.25 MHz, H: 45.000 kHz, V: 60.00 Hz 98 | val m1280x720 = VideoMode( 99 | params = VideoParams( 100 | H_DISPLAY = 1280, H_FRONT = 110, 101 | H_SYNC = 40, H_BACK = 220, 102 | V_SYNC = 5, V_BACK = 20, 103 | V_TOP = 5, V_DISPLAY = 720, 104 | V_BOTTOM = 20 105 | ), 106 | pll = p74250khz 107 | ) 108 | 109 | // D: 71.10 MHz, H: 49.380 kHz, V: 60.00 Hz 110 | val m1280x800 = VideoMode( 111 | params = VideoParams( 112 | H_DISPLAY = 1280, H_FRONT = 48, 113 | H_SYNC = 32, H_BACK = 80, 114 | V_SYNC = 6, V_BACK = 14, 115 | V_TOP = 3, V_DISPLAY = 800, 116 | V_BOTTOM = 14 117 | ), 118 | pll = p71100khz 119 | ) 120 | 121 | // D: 85.50 MHz, H: 47.700 kHz, V: 60.00 Hz 122 | val m1360x768 = VideoMode( 123 | params = VideoParams( 124 | H_DISPLAY = 1360, H_FRONT = 64, 125 | H_SYNC = 112, H_BACK = 256, 126 | V_SYNC = 6, V_BACK = 18, 127 | V_TOP = 3, V_DISPLAY = 768, 128 | V_BOTTOM = 18 129 | ), 130 | pll = p85500khz 131 | ) 132 | 133 | // D: 85.50 MHz, H: 47.880 kHz, V: 60.00 Hz 134 | val m1366x768 = VideoMode( 135 | params = VideoParams( 136 | H_DISPLAY = 1366, H_FRONT = 70, 137 | H_SYNC = 143, H_BACK = 213, 138 | V_SYNC = 3, V_BACK = 24, 139 | V_TOP = 3, V_DISPLAY = 768, 140 | V_BOTTOM = 24 141 | ), 142 | pll = p85500khz 143 | ) 144 | 145 | // D: 106.50 MHz, H: 56.040 kHz, V: 60.00 Hz 146 | val m1440x900 = VideoMode( 147 | params = VideoParams( 148 | H_DISPLAY = 1440, H_FRONT = 80, 149 | H_SYNC = 152, H_BACK = 232, 150 | V_SYNC = 6, V_BACK = 25, 151 | V_TOP = 3, V_DISPLAY = 900, 152 | V_BOTTOM = 25 153 | ), 154 | pll = p106500khz 155 | ) 156 | 157 | // D: 108.00 MHz, H: 63.981 kHz, V: 60.02 Hz 158 | val m1280x1024 = VideoMode( 159 | params = VideoParams( 160 | H_DISPLAY = 1280, H_FRONT = 48, 161 | H_SYNC = 112, H_BACK = 248, 162 | V_SYNC = 3, V_BACK = 38, 163 | V_TOP = 1, V_DISPLAY = 1024, 164 | V_BOTTOM = 38 165 | ), 166 | pll = p108000khz 167 | ) 168 | 169 | // D: 108.00 MHz, H: 60.000 kHz, V: 60.00 Hz 170 | val m1600x900 = VideoMode( 171 | params = VideoParams( 172 | H_DISPLAY = 1600, H_FRONT = 24, 173 | H_SYNC = 80, H_BACK = 96, 174 | V_SYNC = 3, V_BACK = 96, 175 | V_TOP = 1, V_DISPLAY = 900, 176 | V_BOTTOM = 96 177 | ), 178 | pll = p108000khz 179 | ) 180 | 181 | // D: 108.00 MHz, H: 53.465 kHz, V: 55.0055 Hz 182 | val m1824x940 = VideoMode( 183 | params = VideoParams( 184 | H_DISPLAY = 1824, H_FRONT = 20, 185 | H_SYNC = 80, H_BACK = 96, 186 | V_SYNC = 10, V_BACK = 20, 187 | V_TOP = 2, V_DISPLAY = 940, 188 | V_BOTTOM = 0 189 | ), 190 | pll = p108000khz 191 | ) 192 | } 193 | -------------------------------------------------------------------------------- /src/main/scala/video/hvsync.scala: -------------------------------------------------------------------------------- 1 | package hdmicore.video 2 | /* Horizontal, vertical Video signals generation 3 | * inspired from fpga4fun 4 | * and Stephen Hugg book "Designing Video Game Hardware in Verilog" 5 | * */ 6 | 7 | import chisel3._ 8 | import circt.stage.ChiselStage 9 | import chisel3.util._ 10 | 11 | case class VideoParams( 12 | val H_DISPLAY: Int,// horizontal display width 13 | val H_FRONT: Int, // front porch 14 | val H_SYNC: Int, // sync width 15 | val H_BACK: Int, // back porch 16 | val V_SYNC: Int, // sync width 17 | val V_BACK: Int, // back porch 18 | val V_TOP: Int, // top border 19 | val V_DISPLAY: Int,// vertical display width 20 | val V_BOTTOM: Int // bottom border 21 | ) 22 | 23 | class HVSync(val vp: VideoParams = VideoParams( 24 | H_DISPLAY = 640, H_FRONT = 8, H_SYNC = 96, H_BACK = 40, 25 | V_SYNC = 4, V_BACK = 25, V_TOP = 4, V_DISPLAY = 480, V_BOTTOM = 14 26 | )) extends Module { // with Formal { scala version problem 27 | 28 | val hregsize = log2Ceil(vp.H_DISPLAY + vp.H_BACK + vp.H_FRONT + vp.H_SYNC) 29 | val vregsize = log2Ceil(vp.V_DISPLAY + vp.V_TOP + vp.V_BOTTOM + vp.V_SYNC) 30 | val io = IO(new Bundle { 31 | val hsync = Output(Bool()) 32 | val vsync = Output(Bool()) 33 | val display_on = Output(Bool()) 34 | val hpos = Output(UInt(hregsize.W)) 35 | val vpos = Output(UInt(vregsize.W)) 36 | }) 37 | 38 | val H_DISPLAY = vp.H_DISPLAY.U // horizontal display width 39 | val H_FRONT = vp.H_FRONT.U // front porch 40 | val H_SYNC = vp.H_SYNC.U // sync width 41 | val H_BACK = vp.H_BACK.U // back porch 42 | val V_SYNC = vp.V_SYNC.U // sync width 43 | val V_BACK = vp.V_BACK.U // back porch 44 | val V_TOP = vp.V_TOP.U // top border 45 | val V_DISPLAY = vp.V_DISPLAY.U // vertical display width 46 | val V_BOTTOM = vp.V_BOTTOM.U // bottom border 47 | val H_SYNC_START = H_DISPLAY + H_FRONT 48 | val H_SYNC_END = H_DISPLAY + H_FRONT + H_SYNC - 1.U 49 | val H_MAX = H_DISPLAY + H_BACK + H_FRONT + H_SYNC - 1.U 50 | val V_SYNC_START = V_DISPLAY + V_BOTTOM 51 | val V_SYNC_END = V_DISPLAY + V_BOTTOM + V_SYNC - 1.U 52 | val V_MAX = V_DISPLAY + V_TOP + V_BOTTOM + V_SYNC - 1.U 53 | 54 | println(s"H_DISPLAY $H_DISPLAY") 55 | println(s"V_DISPLAY $V_DISPLAY") 56 | println(s"hregsize $hregsize") 57 | println(s"vregsize $vregsize") 58 | 59 | val hpos_count = RegInit(0.U((hregsize).W)) 60 | val vpos_count = RegInit(0.U((vregsize).W)) 61 | io.vpos := vpos_count 62 | io.hpos := hpos_count 63 | 64 | io.display_on := (hpos_count < H_DISPLAY) && (vpos_count < V_DISPLAY) 65 | 66 | /* Horizontal counter */ 67 | io.hsync := !((hpos_count >= H_SYNC_START) && 68 | (hpos_count <= H_SYNC_END)) 69 | 70 | val hpos_max = hpos_count === H_MAX 71 | val vpos_max = vpos_count === V_MAX 72 | 73 | hpos_count := hpos_count + 1.U 74 | when(hpos_max){ 75 | hpos_count := 0.U 76 | } 77 | 78 | /* Vertical counter */ 79 | io.vsync := !((vpos_count >= V_SYNC_START) && 80 | (vpos_count <= V_SYNC_END)) 81 | when(hpos_max) { 82 | vpos_count := vpos_count + 1.U 83 | when(vpos_max) { 84 | vpos_count := 0.U 85 | } 86 | } 87 | } 88 | 89 | object HVSyncDriver extends App { 90 | val verilog_src = ChiselStage 91 | .emitSystemVerilog( 92 | new HVSync, 93 | firtoolOpts = Array( 94 | "-disable-all-randomization", 95 | "--lowering-options=disallowLocalVariables", // avoid 'automatic logic' 96 | "-strip-debug-info")) 97 | val fverilog = os.pwd / "HVSync.v" 98 | if(os.exists(fverilog)) 99 | os.remove(fverilog) 100 | os.write(fverilog, verilog_src) 101 | } 102 | -------------------------------------------------------------------------------- /src/test/python/PatternExample.py: -------------------------------------------------------------------------------- 1 | import os 2 | from struct import pack 3 | from PIL import Image 4 | 5 | class Bitmap(): 6 | def __init__(s, width, height): 7 | s._bfType = 19778 # Bitmap signature 8 | s._bfReserved1 = 0 9 | s._bfReserved2 = 0 10 | s._bcPlanes = 1 11 | s._bcSize = 12 12 | s._bcBitCount = 24 13 | s._bfOffBits = 26 14 | s._bcWidth = width 15 | s._bcHeight = height 16 | s._bfSize = 26+s._bcWidth*3*s._bcHeight 17 | s.clear() 18 | 19 | 20 | def clear(s): 21 | s._graphics = [(0,0,0)]*s._bcWidth*s._bcHeight 22 | 23 | 24 | def setPixel(s, x, y, color): 25 | if isinstance(color, tuple): 26 | if x<0 or y<0 or x>s._bcWidth-1 or y>s._bcHeight-1: 27 | raise ValueError('Coords out of range') 28 | if len(color) != 3: 29 | raise ValueError('Color must be a tuple of 3 elems') 30 | s._graphics[y*s._bcWidth+x] = (color[2], color[1], color[0]) 31 | else: 32 | raise ValueError('Color must be a tuple of 3 elems') 33 | 34 | 35 | def write(s, file): 36 | with open(file, 'wb') as f: 37 | f.write(pack('= (swidth/3)) and (hpos < (swidth*2/3)), 255, 0) 192 | pblue = Mux(hpos < (swidth*2/3), 255, 0) 193 | elif(ptIdx == ptIdxIrishFlag): 194 | swidth = 1280 195 | pred = Mux(hpos < (swidth/3), 0, 255) 196 | pgreen = Mux(hpos < (swidth*2/3), 255, 128) 197 | pblue = Mux((hpos >= (swidth/3)) and (hpos < (swidth*2/3)), 255, 0) 198 | elif(ptIdx == ptIdxItalianFlag): 199 | swidth = 1280 200 | pred = Mux(hpos < (swidth/3), 0, 255) 201 | pgreen = Mux(hpos < (swidth*2/3), 255, 0) 202 | pblue = Mux((hpos >= (swidth/3)) and (hpos < (swidth*2/3)), 255, 0) 203 | elif(ptIdx == ptIdxBelgianFlag): 204 | swidth = 1280 205 | pred = Mux(hpos >= (swidth/3), 255, 0) 206 | pgreen = Mux((hpos >= (swidth/3)) and (hpos < (swidth*2/3)), 255, 0) 207 | pblue = 0 208 | elif(ptIdx == ptIdxDutchFlag): 209 | sheight = 720 210 | prbright = Mux(vpos < (sheight/3), 128, 255) 211 | pbbright = Mux(vpos < (sheight*2/3), 255, 128) 212 | pred = Mux(vpos < (sheight*2/3), prbright, 0) 213 | pgreen = Mux((vpos >= (sheight/3)) and (vpos < (sheight*2/3)), 255, 0) 214 | pblue = Mux(vpos < (sheight/3), 0, pbbright) 215 | elif(ptIdx == ptIdxLuxembourgishFlag): 216 | sheight = 720 217 | pred = Mux(vpos < (sheight*2/3), 255, 0) 218 | pgreen = Mux((vpos >= (sheight/3)) and (vpos < (sheight*2/3)), 255, 0) 219 | pblue = Mux(vpos < (sheight/3), 0, 255) 220 | elif(ptIdx == ptIdxGermanFlag): 221 | sheight = 720 222 | pred = Mux(vpos >= (sheight/3), 255, 0) 223 | pgreen = Mux(vpos < (sheight*2/3), 0, 255) 224 | pblue = 0 225 | elif(ptIdx == ptIdxSpanishFlag): 226 | sheight = 720 227 | pred = 255 228 | pgreen = Mux((vpos >= (sheight/4)) and (vpos < (sheight*3/4)), 255, 0) 229 | pblue = 0 230 | elif(ptIdx == ptIdxAustrianFlag): 231 | sheight = 720 232 | pred = 255 233 | pgreen = Mux((vpos >= (sheight/3)) and (vpos < (sheight*2/3)), 255, 0) 234 | pblue = Mux((vpos >= (sheight/3)) and (vpos < (sheight*2/3)), 255, 0) 235 | elif(ptIdx == ptIdxGreekFlag): 236 | swidth = 1280 237 | sheight = 720 238 | swstep = swidth*3/80 239 | shstep = sheight/9 240 | oinv = Mux((hpos <= (swstep*10)) and (vpos > (shstep*2)) and (vpos <= (shstep*3)), 255, 0) 241 | pinv = Mux((hpos > (swstep*4)) and (hpos <= (swstep*6)) and (vpos <= (shstep*5)), 255, oinv) 242 | ninv = Mux(((hpos <= (swstep*4)) or (hpos > (swstep*6))) and (hpos <= (swstep*10)) and (vpos <= (shstep*5)), 0, 255) 243 | pred = Mux(vpos % (sheight*2/9) > (sheight/9), ninv, pinv) 244 | pgreen = Mux(vpos % (sheight*2/9) > (sheight/9), ninv, pinv) 245 | pblue = 255 246 | elif(ptIdx == ptIdxDanishFlag): 247 | swidth = 1280 248 | sheight = 720 249 | swstep = swidth*4/37 250 | shstep = sheight/7 251 | pinv = Mux((hpos > (swstep*3)) and (hpos <= (swstep*4)), 255, 0) 252 | pred = 255 253 | pgreen = Mux((vpos > (shstep*3)) and (vpos <= (shstep*4)), 255, pinv) 254 | pblue = Mux((vpos > (shstep*3)) and (vpos <= (shstep*4)), 255, pinv) 255 | elif(ptIdx == ptIdxSwedishFlag): 256 | swidth = 1280 257 | sheight = 720 258 | swstep = swidth/16 259 | shstep = sheight/5 260 | pinv = Mux((hpos > (swstep*5)) and (hpos <= (swstep*7)), 255, 0) 261 | ninv = Mux((hpos > (swstep*5)) and (hpos <= (swstep*7)), 0, 255) 262 | pred = Mux((vpos > (shstep*2)) and (vpos <= (shstep*3)), 255, pinv) 263 | pgreen = Mux((vpos > (shstep*2)) and (vpos <= (shstep*3)), 255, pinv) 264 | pblue = Mux((vpos > (shstep*2)) and (vpos <= (shstep*3)), 0, ninv) 265 | elif(ptIdx == ptIdxFinnishFlag): 266 | swidth = 1280 267 | sheight = 720 268 | swstep = swidth/18 269 | shstep = sheight/11 270 | ninv = Mux((hpos > (swstep*5)) and (hpos <= (swstep*8)), 0, 255) 271 | pred = Mux((vpos > (shstep*4)) and (vpos <= (shstep*7)), 0, ninv) 272 | pgreen = Mux((vpos > (shstep*4)) and (vpos <= (shstep*7)), 0, ninv) 273 | pblue = 255 274 | elif(ptIdx == ptIdxNorwegianFlag): 275 | swidth = 1280 276 | sheight = 720 277 | swstep = swidth/22 278 | shstep = sheight/16 279 | minv = Mux((vpos > (shstep*7)) and (vpos <= (shstep*9)), 0, 255) 280 | linv = Mux(((hpos > (swstep*6)) and (hpos <= (swstep*7))) or ((hpos > (swstep*9)) and (hpos <= (swstep*10))), minv, 0) 281 | kinv = Mux((hpos > (swstep*7)) and (hpos <= (swstep*9)), 0, 255) 282 | pgbright = Mux(((vpos > (shstep*6)) and (vpos <= (shstep*7))) or ((vpos > (shstep*9)) and (vpos <= (shstep*10))), kinv, linv) 283 | prbright = Mux(pgbright > 0, 255, 128) 284 | pbbright = Mux(pgbright > 0, 255, 128) 285 | pinv = Mux((hpos > (swstep*6)) and (hpos <= (swstep*10)), pbbright, 0) 286 | ninv = Mux((hpos > (swstep*7)) and (hpos <= (swstep*9)), 0, prbright) 287 | pred = Mux((vpos > (shstep*7)) and (vpos <= (shstep*9)), 0, ninv) 288 | pgreen = Mux(((vpos > (shstep*6)) and (vpos <= (shstep*7))) or ((vpos > (shstep*9)) and (vpos <= (shstep*10))), kinv, linv) 289 | pblue = Mux((vpos > (shstep*6)) and (vpos <= (shstep*10)), pbbright, pinv) 290 | elif(ptIdx == ptIdxUkraineFlag): 291 | # blue #00 57 b7, yellow #ff d7 00 # 292 | sheight = 720 293 | pred = Mux(vpos <= (sheight/2), 0x00, 0xFF) 294 | pgreen= Mux(vpos <= (sheight/2), 0x00, 0xFF) 295 | pblue = Mux(vpos <= (sheight/2), 0xFF, 0x00) 296 | else: 297 | pred = 0 298 | pgreen = 0 299 | pblue = 0 300 | 301 | b.setPixel(hpos, vp.V_DISPLAY-1-vpos, (pred, pgreen, pblue)) 302 | 303 | b.write(n+'.bmp') 304 | Image.open(n+'.bmp').save(n+'.png') 305 | os.remove(n+'.bmp') 306 | 307 | 308 | if __name__ == '__main__': 309 | for p in range(20): 310 | pattern(p) 311 | -------------------------------------------------------------------------------- /src/test/python/austrianflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/austrianflag.png -------------------------------------------------------------------------------- /src/test/python/belgianflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/belgianflag.png -------------------------------------------------------------------------------- /src/test/python/danishflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/danishflag.png -------------------------------------------------------------------------------- /src/test/python/dutchflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/dutchflag.png -------------------------------------------------------------------------------- /src/test/python/finnishflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/finnishflag.png -------------------------------------------------------------------------------- /src/test/python/frenchflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/frenchflag.png -------------------------------------------------------------------------------- /src/test/python/germanflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/germanflag.png -------------------------------------------------------------------------------- /src/test/python/greekflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/greekflag.png -------------------------------------------------------------------------------- /src/test/python/hgradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/hgradient.png -------------------------------------------------------------------------------- /src/test/python/hstripes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/hstripes.png -------------------------------------------------------------------------------- /src/test/python/irishflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/irishflag.png -------------------------------------------------------------------------------- /src/test/python/italianflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/italianflag.png -------------------------------------------------------------------------------- /src/test/python/luxembourgishflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/luxembourgishflag.png -------------------------------------------------------------------------------- /src/test/python/norwegianflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/norwegianflag.png -------------------------------------------------------------------------------- /src/test/python/rainbow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/rainbow.png -------------------------------------------------------------------------------- /src/test/python/scala2py.sh: -------------------------------------------------------------------------------- 1 | cat src/main/scala/PatternExample.scala | \ 2 | sed s/'} \.elsewhen'/'elif'/g | sed s/'} \.otherwise'/'else'/g | \ 3 | sed s/'}\.elsewhen'/'elif'/g | sed s/'}\.otherwise'/'else'/g | \ 4 | sed s/'when'/'if'/g | sed s/'{$'/':'/g | sed s/'==='/'=='/g | sed s/':='/'='/g | \ 5 | sed s/'\.U'//g | sed s/'val '/''/g | sed s/'^ '/' '/g | sed s/'}$'/''/g | \ 6 | sed 's|/\*|#|g' | sed 's|\*/|#|g' | sed 's|// |# |g' | sed 's|//-|#-|g' | \ 7 | sed s/'!'/'not '/g | sed s/'&&'/'and'/g | sed s/'||'/'or'/g | \ 8 | sed s/'"h00"'/'0x00'/g | sed s/'"hFF"'/'0xFF'/g 9 | -------------------------------------------------------------------------------- /src/test/python/spanishflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/spanishflag.png -------------------------------------------------------------------------------- /src/test/python/swedishflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/swedishflag.png -------------------------------------------------------------------------------- /src/test/python/ukraineflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/ukraineflag.png -------------------------------------------------------------------------------- /src/test/python/vgradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/vgradient.png -------------------------------------------------------------------------------- /src/test/python/vstripes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martoni/HdmiCore/da5cdfd2594fe764412bf7f3185d70a9536272ad/src/test/python/vstripes.png -------------------------------------------------------------------------------- /src/test/scala/TmdsEncoder.scala: -------------------------------------------------------------------------------- 1 | package hdmicore 2 | 3 | import org.scalatest._ 4 | import chiseltest._ 5 | import chiseltest.formal._ 6 | import chisel3._ 7 | import chisel3.util.PopCount 8 | import chisel3.util._ 9 | import org.scalatest.flatspec.AnyFlatSpec 10 | 11 | import scala.util.control.Breaks._ 12 | import java.io._ 13 | import scala.util.Random 14 | 15 | 16 | /** 17 | * TMDSEncoder class test 18 | * For documentation about TMDS encoding see wikipedia : 19 | * https://en.wikipedia.org/wiki/Transition-minimized_differential_signaling 20 | * Or this google doc 21 | * https://docs.google.com/document/d/1v7AJK4cVG3uDJo_rn0X9vxMvBwXKBSL1VaJgiXgFo5A/edit# 22 | */ 23 | 24 | class TMDSEncoderSpec extends FlatSpec with ChiselScalatestTester with Matchers { 25 | behavior of "TMDSEncoder" 26 | 27 | it should " return fixed patterns if de is false " in { 28 | test(new TMDSEncoder()) { dut => 29 | dut.io.en.poke(false.B) 30 | dut.io.ctrl.poke(0.U) 31 | dut.io.din.poke(0.U) 32 | dut.clock.step(1) 33 | dut.io.dout.expect("b1101010100".U(10.W)) 34 | dut.io.ctrl.poke(1.U) 35 | dut.clock.step(1) 36 | dut.io.dout.expect("b0010101011".U(10.W)) 37 | dut.io.ctrl.poke(2.U) 38 | dut.clock.step(1) 39 | dut.io.dout.expect("b0101010100".U(10.W)) 40 | dut.io.ctrl.poke(3.U) 41 | dut.clock.step(1) 42 | dut.io.dout.expect("b1010101011".U(10.W)) 43 | } 44 | } 45 | 46 | 47 | 48 | } 49 | 50 | /** cvc4 is required for this test 51 | * https://cvc4.github.io/ 52 | */ 53 | 54 | class TMDSFormalSpec extends Module { 55 | val dut = Module(new TMDSEncoder) 56 | val io = IO(chiselTypeOf(dut.io)) 57 | io <> dut.io 58 | 59 | /* default control frame when disable (io.en === false.B) */ 60 | val resetValid = RegInit(false.B) 61 | resetValid := true.B 62 | when(past(dut.io.en === false.B) && resetValid){ 63 | switch(past(dut.io.ctrl)){ 64 | is("b00".U(2.W)){assert(dut.io.dout === "b1101010100".U(10.W))} 65 | is("b01".U(2.W)){assert(dut.io.dout === "b0010101011".U(10.W))} 66 | is("b10".U(2.W)){assert(dut.io.dout === "b0101010100".U(10.W))} 67 | is("b11".U(2.W)){assert(dut.io.dout === "b1010101011".U(10.W))} 68 | } 69 | } 70 | 71 | // the number of ones and zeros should always be balanced 72 | // val ones = dontTouch(WireInit(PopCount(dut.io.dout))) 73 | // assert(ones === 5.U || ones === 6.U) 74 | 75 | } 76 | 77 | class TMDSEncoderFormalTest extends AnyFlatSpec with ChiselScalatestTester with Formal { 78 | "TMDSEncoder" should "pass a bounded check" in { 79 | verify(new TMDSFormalSpec, Seq(BoundedCheck(4), CVC4EngineAnnotation)) 80 | } 81 | } 82 | 83 | --------------------------------------------------------------------------------