├── .github └── workflows │ └── build-SoC.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── extra ├── boot-setup │ ├── DiVA-foboot.bit │ ├── DiVA-fw.bit │ ├── bin │ │ ├── dfu-util │ │ ├── dfu-util.exe │ │ ├── ecpprog │ │ └── ecpprog.exe │ ├── doc │ │ ├── cmd_001.PNG │ │ ├── cmd_002.PNG │ │ ├── dfu-util_001.PNG │ │ ├── dfu-util_002.PNG │ │ ├── zadig_001.PNG │ │ ├── zadig_002.PNG │ │ └── zadig_003.PNG │ └── readme.md └── udev-rules │ └── 20-diva-bootloader.rules ├── firmware └── DiVA-fw │ ├── Makefile │ ├── boson.c │ ├── boson │ ├── FSLP.h │ ├── FunctionCodes.h │ ├── ReturnCodes.h │ ├── Serializer_BuiltIn.c │ ├── Serializer_BuiltIn.h │ ├── flirCRC.c │ └── flirCRC.h │ ├── crc.c │ ├── dma.c │ ├── hdmi_edid.c │ ├── hyperram.c │ ├── i2c.c │ ├── include │ ├── boson.h │ ├── crc.h │ ├── dma.h │ ├── hdmi_edid.h │ ├── hyperram.h │ ├── i2c.h │ ├── scaler.h │ ├── settings.h │ ├── terminal.h │ ├── terminal_menu.h │ └── time.h │ ├── linker.ld │ ├── main.c │ ├── scaler.c │ ├── settings.c │ ├── terminal.c │ ├── terminal_menu.c │ └── time.c ├── gateware ├── DiVA-Bitstream.py ├── lxbuildenv.py ├── rtl │ ├── buffered_csr_block.py │ ├── buttons.py │ ├── ecp5_dynamic_pll.py │ ├── edge_detect.py │ ├── hdmi.py │ ├── platform │ │ ├── bosonHDMI_r0d2.py │ │ └── bosonHDMI_r0d3.py │ ├── prbs.py │ ├── reboot.py │ ├── rgb_led.py │ ├── streamable_hyperram.py │ ├── verilog │ │ ├── fake_differential.v │ │ ├── tmds_encoder.v │ │ └── vga2dvid.v │ ├── video │ │ ├── YCrCb.py │ │ ├── boson.py │ │ ├── framer.py │ │ ├── scaler.py │ │ ├── scaler_coeff_gen.py │ │ ├── simulated_video.py │ │ ├── stream_utils.py │ │ ├── terminal.py │ │ ├── video_debug.py │ │ └── video_stream.py │ └── wb_streamer.py └── util │ ├── colours.png │ └── cp437.bin └── software └── fw-updater ├── App.config ├── Form1.Designer.cs ├── Form1.cs ├── Form1.resx ├── Program.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── Resources ├── 0173-hdd-down.png ├── 0853-notification.png ├── 0858-checkmark-circle.png ├── 0859-cross-circle.png ├── dfu_util.exe └── fw-update.ico ├── fw-updater.csproj └── fw-updater.sln /.github/workflows/build-SoC.yml: -------------------------------------------------------------------------------- 1 | name: build-SoC 2 | 3 | on: 4 | push: 5 | 6 | 7 | jobs: 8 | # Main build, creates firmware update image 9 | build-soc: 10 | runs-on: ubuntu-18.04 11 | steps: 12 | - uses: actions/checkout@v2 13 | with: 14 | submodules: false # Note: not Recursive (Causes error with serv) 15 | 16 | - name: submodule init 17 | run: git submodule init 18 | 19 | - name: configure fpga-toolchain 20 | run: | 21 | export VERSION=nightly-20201020 22 | wget https://github.com/open-tool-forge/fpga-toolchain/releases/download/$VERSION/fpga-toolchain-linux_x86_64-$VERSION.tar.xz 23 | tar -xf fpga-toolchain-linux_x86_64-$VERSION.tar.xz 24 | rm -rf fpga-toolchain-linux_x86_64-$VERSION.tar.xz 25 | echo "$(pwd)/fpga-toolchain/bin" >> $GITHUB_PATH 26 | 27 | - name: configure riscv-toolchain 28 | run: | 29 | wget https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14.tar.gz 30 | tar -xzf riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14.tar.gz 31 | rm -rf riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14.tar.gz 32 | echo "$(pwd)/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14/bin" >> $GITHUB_PATH 33 | 34 | - name: configure python 35 | run: | 36 | python3 -m pip install setuptools 37 | python3 -m pip install pycrc 38 | python3 -m pip install Pillow 39 | python3 -m pip install wheel 40 | python3 -m pip install Sphinx 41 | python3 -m pip install sphinxcontrib-wavedrom 42 | 43 | 44 | - name: Litex build 45 | working-directory: ./gateware 46 | run: | 47 | python3 DiVA-Bitstream.py 48 | 49 | - name: Upload build 50 | uses: actions/upload-artifact@v2 51 | with: 52 | name: DiVA build Folder 53 | path: ${{ github.workspace }}/gateware/build 54 | 55 | - name: Upload dfu update file 56 | uses: actions/upload-artifact@v2 57 | with: 58 | name: DiVA firmware update 59 | path: ${{ github.workspace }}/gateware/build/gateware/DiVA.dfu 60 | 61 | # Software build, create windows app to perform the dfu update 62 | build-sw: 63 | runs-on: windows-latest 64 | needs: build-soc 65 | steps: 66 | - uses: actions/checkout@master 67 | 68 | - name: Download DFU Binary 69 | uses: actions/download-artifact@v2 70 | with: 71 | name: DiVA firmware update 72 | 73 | - shell: bash 74 | run: mv DiVA.dfu software/fw-updater/Resources/DiVA.dfu 75 | 76 | - name: Add msbuild to PATH 77 | uses: microsoft/setup-msbuild@v1.0.2 78 | 79 | - name: MSBuild 80 | working-directory: software/fw-updater 81 | run: msbuild fw-updater.csproj /p:Configuration=Release 82 | 83 | - name: Upload Updater 84 | uses: actions/upload-artifact@v2 85 | with: 86 | name: Windows update tool 87 | path: software/fw-updater/bin/Release/fw-updater.exe 88 | 89 | release: 90 | name: Create Release 91 | needs: build-sw 92 | if: ${{ contains( github.ref, 'refs/tags/') }} 93 | runs-on: ubuntu-latest 94 | steps: 95 | #- name: Checkout code 96 | # uses: actions/checkout@v2 97 | - name: Download DFU Binary 98 | uses: actions/download-artifact@v2 99 | with: 100 | name: DiVA firmware update 101 | 102 | - name: Download DFU Binary 103 | uses: actions/download-artifact@v2 104 | with: 105 | name: Windows update tool 106 | 107 | - name: Create Release 108 | id: create-release 109 | uses: actions/create-release@v1 110 | env: 111 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token 112 | with: 113 | tag_name: ${{ github.ref }} 114 | release_name: Release ${{ github.ref }} 115 | body: | 116 | Changes in this Release 117 | - First Change 118 | - Second Change 119 | draft: true 120 | prerelease: false 121 | - name: Upload Release Asset 122 | uses: actions/upload-release-asset@v1 123 | env: 124 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 125 | with: 126 | upload_url: ${{ steps.create-release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 127 | asset_path: ./DiVA.dfu 128 | asset_name: DiVA.dfu 129 | asset_content_type: application/octet-stream 130 | 131 | - name: Upload Release Asset 132 | uses: actions/upload-release-asset@v1 133 | env: 134 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 135 | with: 136 | upload_url: ${{ steps.create-release.outputs.upload_url }} 137 | asset_path: ./fw-updater.exe 138 | asset_name: fw-updater.exe 139 | asset_content_type: application/octet-stream 140 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | __pycache__ 3 | settings.json 4 | 5 | *.vcd 6 | *.vvp 7 | 8 | sim_build/ 9 | results.xml 10 | 11 | analyzer.csv 12 | 13 | .vs/ 14 | obj/ 15 | bin/ 16 | *csproj.user 17 | software/fw-updater/Resources/DiVA.dfu 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "gateware/deps/valentyusb"] 2 | path = gateware/deps/valentyusb 3 | url = https://github.com/gregdavill/valentyusb 4 | [submodule "gateware/deps/litex"] 5 | path = gateware/deps/litex 6 | url = https://github.com/enjoy-digital/litex 7 | [submodule "gateware/deps/migen"] 8 | path = gateware/deps/migen 9 | url = https://github.com/m-labs/migen 10 | [submodule "gateware/deps/pythondata-software-compiler_rt"] 11 | path = gateware/deps/pythondata-software-compiler_rt 12 | url = https://github.com/litex-hub/pythondata-software-compiler_rt.git 13 | [submodule "gateware/deps/litescope"] 14 | path = gateware/deps/litescope 15 | url = https://github.com/enjoy-digital/litescope.git 16 | [submodule "gateware/deps/pythondata-cpu-serv"] 17 | path = gateware/deps/pythondata-cpu-serv 18 | url = https://github.com/litex-hub/pythondata-cpu-serv.git 19 | [submodule "gateware/deps/litehyperbus"] 20 | path = gateware/deps/litehyperbus 21 | url = https://github.com/litex-hub/litehyperbus.git 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2020-2021 Greg Davill 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /extra/boot-setup/DiVA-foboot.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/extra/boot-setup/DiVA-foboot.bit -------------------------------------------------------------------------------- /extra/boot-setup/DiVA-fw.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/extra/boot-setup/DiVA-fw.bit -------------------------------------------------------------------------------- /extra/boot-setup/bin/dfu-util: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/extra/boot-setup/bin/dfu-util -------------------------------------------------------------------------------- /extra/boot-setup/bin/dfu-util.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/extra/boot-setup/bin/dfu-util.exe -------------------------------------------------------------------------------- /extra/boot-setup/bin/ecpprog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/extra/boot-setup/bin/ecpprog -------------------------------------------------------------------------------- /extra/boot-setup/bin/ecpprog.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/extra/boot-setup/bin/ecpprog.exe -------------------------------------------------------------------------------- /extra/boot-setup/doc/cmd_001.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/extra/boot-setup/doc/cmd_001.PNG -------------------------------------------------------------------------------- /extra/boot-setup/doc/cmd_002.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/extra/boot-setup/doc/cmd_002.PNG -------------------------------------------------------------------------------- /extra/boot-setup/doc/dfu-util_001.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/extra/boot-setup/doc/dfu-util_001.PNG -------------------------------------------------------------------------------- /extra/boot-setup/doc/dfu-util_002.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/extra/boot-setup/doc/dfu-util_002.PNG -------------------------------------------------------------------------------- /extra/boot-setup/doc/zadig_001.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/extra/boot-setup/doc/zadig_001.PNG -------------------------------------------------------------------------------- /extra/boot-setup/doc/zadig_002.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/extra/boot-setup/doc/zadig_002.PNG -------------------------------------------------------------------------------- /extra/boot-setup/doc/zadig_003.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/extra/boot-setup/doc/zadig_003.PNG -------------------------------------------------------------------------------- /extra/boot-setup/readme.md: -------------------------------------------------------------------------------- 1 | # DiVA Initial Setup Guide 2 | 3 | DiVA or the Digital Video Adapter is an addon board for the Boson LWIR core from FLIR. 4 | It features an FPGA that performs the video conversion from the camera to a Digital Video Interface. In order for this to function you will need to load the initial firmware onto the device. 5 | 6 | Additionally the board is designed to be user updateable in the field over just a USB connection. In order to accomplish this we need to also load a bootloader onto the device, we'll do this first. 7 | 8 | In order to simplify everything, tools from [Open Tool Forge](https://github.com/open-tool-forge/fpga-toolchain/releases) have been included under `/bin` for Linux and Windows 9 | 10 | ## Initial device programming 11 | 12 | 1. Connect the programming jig to the computer. 13 | 14 | 15 | 2. Place a PCB into the programer, do not connect USB to the PCB, the programmer will supply power. 16 | 17 | 18 | 3. Run this command: 19 | 20 | Linux: 21 | ```console 22 | $ bin/ecpprog DiVA-foboot.bit 23 | ``` 24 | Windows: 25 | ```console 26 | > bin\ecpprog.exe DiVA-foboot.bit 27 | ``` 28 | 29 | 4. Remove the board from the programmer, and then connect it via USB, while holding down the top button. 30 | 31 | 32 | 5. Run dfu-util to load the application firmware and gateware. 33 | 34 | Linux: 35 | ```console 36 | $ bin/dfu-util -D DiVA-fw.bit 37 | ``` 38 | Windows: 39 | ```console 40 | > bin\dfu-util.exe -D DiVA-fw.bit 41 | ``` 42 | 43 | 44 | The board will now have a bootloader and it's application firmware loaded. 45 | 46 | ## Further testing 47 | 48 | 1. Connect a cable from the board to a monitor, you sholud see an image on the screen 49 | 2. Unplug from USB and connect a camera to the board, and fasten the board with 3 M1.6 screws. 50 | 3. When you power the board back up you should see an image from the thermal camera, pressing the bottom button will cycle through the on-board palletes on the camera, and holding the bottom button will toggle full-screen scaling. 51 | 52 | 53 | ## Troubleshooting 54 | 55 | If you have errors using `dfu-util`, on linux you may need to load some udev rules, using this command, then reconnect the board: 56 | ```console 57 | $ cp extra/udev-rules/20-diva-bootloader.rules /etc/udev/rules.d/ 58 | ``` 59 | 60 | --- 61 | 62 | When you connect DiVA while holding down the top button the LED should be cycling through Red > Green > Blue. And you should see a DFU device appear 63 | ``` console 64 | $ dmesg | tail 65 | [970.951694] usb 3-1.3: new full-speed USB device number 7 using xhci_hcd 66 | [971.104391] usb 3-1.3: New USB device found, idVendor=16d0, idProduct=0fad, bcdDevice= 1.01 67 | [971.104403] usb 3-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0 68 | [971.104410] usb 3-1.3: Product: Boson DiVA r0.3 - DFU Bootloader v3.1-4-g4d46328 69 | [971.104417] usb 3-1.3: Manufacturer: Get Labs 70 | ``` 71 | 72 | ---- 73 | ### Windows Notes 74 | 75 | On windows you will need to append `.exe` to each command. You will also need to install libusbK drivers for the ftdi adapter. You can run these from either a WSL bash console, Command Prompt or PowerShell. 76 | 77 | ### Install LibusbK dirvers using Zadig 78 | 79 | 1. Download and open [Zadig](https://zadig.akeo.ie/) 80 | 81 | 2. "List all devices" to show the FTDI device 82 | 83 | ![Zadig Option](doc/zadig_001.PNG "Zadig Option") 84 | 85 | 3. Ensure that `0403:6014` or `0403:6010` is the PID/VID and select libusbK as the driver, click Replace Driver 86 | 87 | ![Zadig Replace](doc/zadig_002.PNG "Zadig Replace") 88 | 89 | 4. Success! 90 | 91 | ![Zadig Success](doc/zadig_003.PNG "Zadig Success") 92 | 93 | ---- 94 | ### ecpprog output 95 | 96 | Confirm the new libusb drivers work, you should see a valid IDCODE. 97 | ![ecpprog test](doc/cmd_001.PNG "ecpprog test") 98 | ![ecpprog DONE](doc/cmd_002.PNG "ecpprog DONE") 99 | 100 | ---- 101 | ### dfu-util output 102 | 103 | Use the dfu-util --list` option to test if the DFU device is attached 104 | ![dfu-util test](doc/dfu-util_001.PNG "dfu-util test") 105 | ![dfu-util done](doc/dfu-util_002.PNG "dfu-util done") 106 | 107 | -------------------------------------------------------------------------------- /extra/udev-rules/20-diva-bootloader.rules: -------------------------------------------------------------------------------- 1 | ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0fad", MODE="0666", GROUP="plugdev", TAG+="uaccess" 2 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/Makefile: -------------------------------------------------------------------------------- 1 | include ../include/generated/variables.mak 2 | include $(SOC_DIRECTORY)/software/common.mak 3 | 4 | CFLAGS += -fpack-struct -O3 -I$(DIVA-FW_DIRECTORY)/include -fdata-sections -ffunction-sections 5 | 6 | OBJECTS=main.o time.o hyperram.o hdmi_edid.o terminal.o i2c.o boson.o settings.o flirCRC.o Serializer_BuiltIn.o terminal_menu.o dma.o scaler.o 7 | 8 | vpath % $(DIVA-FW_DIRECTORY) $(DIVA-FW_DIRECTORY)/boson 9 | 10 | all: DiVA-fw.bin size 11 | 12 | %.bin: %.elf 13 | $(OBJCOPY) -O binary $< $@ 14 | ifneq ($(OS),Windows_NT) 15 | chmod -x $@ 16 | endif 17 | ifeq ($(CPUENDIANNESS),little) 18 | $(PYTHON) -m litex.soc.software.mkmscimg $@ --little 19 | else 20 | $(PYTHON) -m litex.soc.software.mkmscimg $@ 21 | endif 22 | 23 | DiVA-fw.elf: $(DIVA-FW_DIRECTORY)/linker.ld $(OBJECTS) 24 | 25 | %.elf: 26 | $(LD) $(LDFLAGS) -L$(BUILDINC_DIRECTORY)/generated -T $(DIVA-FW_DIRECTORY)/linker.ld -N -o $@ \ 27 | ../libbase/crt0.o \ 28 | $(OBJECTS) \ 29 | -gc-sections \ 30 | -L../libbase \ 31 | -L../libcompiler_rt \ 32 | -lbase-nofloat -lcompiler_rt 33 | ifneq ($(OS),Windows_NT) 34 | chmod -x $@ 35 | endif 36 | 37 | # pull in dependency info for *existing* .o files 38 | -include $(OBJECTS:.o=.d) 39 | 40 | %.o: %.c 41 | $(compile) 42 | 43 | %.o: %.S 44 | $(assemble) 45 | 46 | clean: 47 | $(RM) $(OBJECTS) DiVA-fw.elf DiVA-fw.bin .*~ *~ 48 | 49 | size: 50 | $(TARGET_PREFIX)size DiVA-fw.elf 51 | 52 | 53 | .PHONY: all clean 54 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/boson.c: -------------------------------------------------------------------------------- 1 | #include "include/boson.h" 2 | #include "include/time.h" 3 | #include "include/settings.h" 4 | #include 5 | 6 | volatile static uint16_t boson_crc; 7 | 8 | volatile static uint32_t _seq = 0; 9 | 10 | #define UINT32_LE(a) (a << 24) & 0xFF, (a << 16) & 0xFF, (a << 8) & 0xFF, a & 0xFF 11 | 12 | #define UART_EV_TX 0x1 13 | #define UART_EV_RX 0x2 14 | 15 | /* Prototypes */ 16 | static uint8_t boson_uart_read(void); 17 | static int boson_uart_read_nonblock(void); 18 | static void boson_uart_write(uint8_t c); 19 | static void boson_uart_write_escaped(uint8_t c); 20 | static void boson_uart_init(void); 21 | static void boson_uart_sync(void); 22 | static void boson_uart_write_array(const uint8_t* array, uint32_t len); 23 | 24 | 25 | static uint8_t boson_uart_read(void) 26 | { 27 | uint8_t c; 28 | while (boson_uart_rxempty_read()); 29 | c = boson_uart_rxtx_read(); 30 | boson_uart_ev_pending_write(UART_EV_RX); 31 | return c; 32 | } 33 | 34 | static int boson_uart_read_nonblock(void) 35 | { 36 | return (boson_uart_rxempty_read() == 0); 37 | } 38 | 39 | static void boson_uart_write(uint8_t c) 40 | { 41 | boson_crc = ByteCRC16((int)c, boson_crc); 42 | while (boson_uart_txfull_read()); 43 | boson_uart_rxtx_write(c); 44 | boson_uart_ev_pending_write(UART_EV_TX); 45 | } 46 | 47 | static void boson_uart_write_escaped(uint8_t c) 48 | { 49 | if ((c == START_FRAME_BYTE) || (c == END_FRAME_BYTE) || (c == ESCAPE_BYTE)){ 50 | boson_uart_write(ESCAPE_BYTE); 51 | 52 | switch (c) 53 | { 54 | case END_FRAME_BYTE: 55 | c = ESCAPED_END_FRAME_BYTE; 56 | break; 57 | case START_FRAME_BYTE: 58 | c = ESCAPED_START_FRAME_BYTE; 59 | break; 60 | case ESCAPE_BYTE: 61 | c = ESCAPED_ESCAPE_BYTE; 62 | break; 63 | default: 64 | break; 65 | } 66 | } 67 | 68 | boson_uart_write(c); 69 | } 70 | 71 | 72 | 73 | static void boson_uart_init(void) 74 | { 75 | boson_uart_ev_pending_write(boson_uart_ev_pending_read()); 76 | boson_uart_ev_enable_write(UART_EV_TX | UART_EV_RX); 77 | } 78 | 79 | static void boson_uart_sync(void) 80 | { 81 | while (boson_uart_txfull_read()); 82 | } 83 | 84 | 85 | static void boson_uart_write_array(const uint8_t* array, uint32_t len){ 86 | for(int i = 0; i < len; i++){ 87 | boson_uart_write_escaped(array[i]); 88 | } 89 | } 90 | 91 | /* Similar to the FLIR SDK functions, but without buffering */ 92 | FLR_RESULT dispatcher_tx(uint32_t seqNum, FLR_FUNCTION fnID, const uint8_t *sendData, const uint32_t sendBytes) { 93 | 94 | uint8_t tmp_array[4]; 95 | 96 | boson_uart_write(START_FRAME_BYTE); 97 | 98 | /* Reset CRC, START_FRAME_BYTE not included in calculation */ 99 | boson_crc = FLIR_CRC_INITIAL_VALUE; 100 | boson_uart_write(0); /* Channel ID: Command Channel */ 101 | 102 | /* Send Sequence number */ 103 | UINT_32ToByte(seqNum, (const uint8_t *)tmp_array); 104 | boson_uart_write_array(tmp_array, 4); 105 | 106 | 107 | /* Send function ID */ 108 | UINT_32ToByte((const uint32_t) fnID, (const uint8_t *)tmp_array); 109 | boson_uart_write_array(tmp_array, 4); 110 | 111 | /* Send 0xFFFFFFFF */ 112 | UINT_32ToByte(0xFFFFFFFF, (const uint8_t *)tmp_array); 113 | boson_uart_write_array(tmp_array, 4); 114 | 115 | 116 | /* Send sendData */ 117 | if(sendBytes > 0){ 118 | boson_uart_write_array(sendData, sendBytes); 119 | } 120 | 121 | /* Send out the CRC */ 122 | uint8_t crcbyte0 = ((boson_crc >> 8) & 0xFF); 123 | uint8_t crcbyte1 = (boson_crc & 0xFF); 124 | boson_uart_write_escaped(crcbyte0); 125 | boson_uart_write_escaped(crcbyte1); 126 | 127 | boson_uart_write(END_FRAME_BYTE); 128 | 129 | return R_SUCCESS; 130 | 131 | 132 | } 133 | 134 | FLR_RESULT dispatcher_rx(uint8_t* recvData, uint32_t* recvBytes) { 135 | 136 | /* Setup a timeout interval */ 137 | int timeout = 200; 138 | int timeout_count = 0; 139 | 140 | FLR_RESULT errorCode = R_UART_RECEIVE_TIMEOUT; 141 | 142 | uint32_t max_len = *recvBytes; 143 | *recvBytes = 0; 144 | 145 | uint32_t len = 0; 146 | 147 | //printf("bsn << "); 148 | while((++timeout_count < timeout) && (errorCode != R_SUCCESS)){ 149 | 150 | while(boson_uart_read_nonblock()) { 151 | recvData[len] = boson_uart_read(); 152 | 153 | /* Skip forward looking for START_OF_FRAME */ 154 | if(len == 0){ 155 | if(recvData[0] != START_FRAME_BYTE) 156 | continue; 157 | } 158 | 159 | //printf("%02x ", recvData[len]); 160 | 161 | if(len > 2){ 162 | if(recvData[len] == END_FRAME_BYTE){ 163 | if(recvData[len-1] != ESCAPED_END_FRAME_BYTE){ 164 | 165 | /* End of frame */ 166 | errorCode = R_SUCCESS; 167 | *recvBytes = len + 1; 168 | break; 169 | } 170 | } 171 | } 172 | 173 | 174 | /* Basic bounds protection */ 175 | if(len < max_len){ 176 | len++; 177 | }else{ 178 | errorCode = R_SDK_PKG_BUFFER_OVERFLOW; 179 | break; 180 | } 181 | } 182 | 183 | msleep(1); 184 | } 185 | 186 | // printf("(%u ms)\n", timeout_count); 187 | 188 | return errorCode; 189 | } 190 | 191 | FLR_RESULT dispatcher(FLR_FUNCTION fnID, const uint8_t *sendData, const uint32_t sendBytes){ 192 | FLR_RESULT r = R_SYM_UNSPECIFIED_FAILURE; 193 | uint8_t recvData[64]; 194 | uint32_t recvBytes = (sizeof(recvData)/sizeof(uint8_t)); 195 | uint32_t seq = _seq++; 196 | 197 | r = dispatcher_tx(seq, fnID, sendData, sendBytes); 198 | if(r != R_SUCCESS){ 199 | return r; 200 | } 201 | 202 | boson_uart_sync(); 203 | 204 | /* Listen to the RX bytes, to check error code. */ 205 | r = dispatcher_rx(recvData, &recvBytes); 206 | if(r == R_SUCCESS){ 207 | /* Check CRC */ 208 | if(recvBytes > 4){ 209 | uint16_t calcCRC = calcFlirCRC16Bytes(recvBytes - 2, recvData + 1); 210 | if(calcCRC != 0){ 211 | return R_UART_UNSPECIFIED_FAILURE; 212 | } 213 | } 214 | /* Check seq number */ 215 | uint32_t rx_seq = 0; 216 | byteToUINT_32(recvData + 2, &rx_seq); 217 | if(seq != rx_seq){ 218 | return R_UART_UNSPECIFIED_FAILURE; 219 | } 220 | 221 | /* Check result code */ 222 | byteToUINT_32(recvData + 10, &r); 223 | } 224 | 225 | return r; 226 | } 227 | 228 | 229 | void boson_init(void){ 230 | 231 | boson_uart_init(); 232 | msleep(3500); 233 | boson_uart_write(0); 234 | boson_uart_write(0); 235 | 236 | 237 | /* Ping the camera a few times, takes ~3s after bootup before it will respond */ 238 | FLR_RESULT r = R_UART_RECEIVE_TIMEOUT; 239 | for(int tries = 10; tries > 0 && r != R_SUCCESS; tries--){ 240 | r = dispatcher(BOSON_GETCAMERASN, 0, 0); 241 | } 242 | 243 | if(r == R_SUCCESS){ 244 | /* Basic setup of boson core */ 245 | dispatcher(DVO_SETDISPLAYMODE, (const uint8_t[]){UINT32_LE(0)}, 4); 246 | dispatcher(TELEMETRY_SETSTATE, (const uint8_t[]){UINT32_LE(0)}, 4); /* Disable Telemetry Line */ 247 | dispatcher(DVO_SETANALOGVIDEOSTATE, (const uint8_t[]){UINT32_LE(0)}, 4); 248 | dispatcher(DVO_SETOUTPUTFORMAT, (const uint8_t[]){UINT32_LE(1)}, 4); /* Mode: YCbCr */ 249 | dispatcher(DVO_SETOUTPUTYCBCRSETTINGS, (const uint8_t[]){UINT32_LE(0),UINT32_LE(1),UINT32_LE(0)}, 12); /* CbCr Order: Cb -> Cr */ 250 | dispatcher(DVO_SETTYPE, (const uint8_t[]){UINT32_LE(2)}, 4); /* Type: COLOUR */ 251 | dispatcher(DVO_APPLYCUSTOMSETTINGS, 0, 0); 252 | dispatcher(COLORLUT_SETID, (const uint8_t[]){UINT32_LE(_settings.pallete)}, 4); /* Colour LUT: Ironbow */ 253 | dispatcher(COLORLUT_SETCONTROL, (const uint8_t[]){UINT32_LE(1)}, 4); 254 | dispatcher(GAO_SETAVERAGERSTATE, (const uint8_t[]){UINT32_LE(_settings.averager)}, 4); 255 | 256 | msleep(500); 257 | dispatcher(BOSON_RUNFFC, 0, 0); 258 | } 259 | } 260 | 261 | 262 | void boson_set_lut(uint32_t lut){ 263 | dispatcher(COLORLUT_SETID, (const uint8_t[]){0x00, 0x00, 0x00, lut}, 4); /* Colour LUT: Ironbow */ 264 | } 265 | 266 | void boson_set_averager(uint32_t en){ 267 | dispatcher(GAO_SETAVERAGERSTATE, (const uint8_t[]){UINT32_LE(en)}, 4); 268 | } 269 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/boson/FSLP.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #define ESCAPE_BYTE 0x9E 5 | #define START_FRAME_BYTE 0x8E 6 | #define END_FRAME_BYTE 0xAE 7 | #define ESCAPED_ESCAPE_BYTE 0x91 8 | #define ESCAPED_START_FRAME_BYTE 0x81 9 | #define ESCAPED_END_FRAME_BYTE 0xA1 10 | 11 | #define NUM_FRAMING_BYTES 3 12 | #define FRAME_START_IDX 1 13 | #define CRC_START_IDX 0 -------------------------------------------------------------------------------- /firmware/DiVA-fw/boson/ReturnCodes.h: -------------------------------------------------------------------------------- 1 | // ///////////////////////////////////////////////////// 2 | // // DO NOT EDIT. This is a machine generated file. // 3 | // ///////////////////////////////////////////////////// 4 | /******************************************************************************/ 5 | /* */ 6 | /* Copyright (C) 2018, FLIR Systems */ 7 | /* All rights reserved. */ 8 | /* */ 9 | /* This document is controlled to FLIR Technology Level 2. The information */ 10 | /* contained in this document pertains to a dual use product controlled for */ 11 | /* export by the Export Administration Regulations (EAR). Diversion contrary */ 12 | /* to US law is prohibited. US Department of Commerce authorization is not */ 13 | /* required prior to export or transfer to foreign persons or parties unless */ 14 | /* otherwise prohibited. */ 15 | /* */ 16 | /******************************************************************************/ 17 | 18 | #ifndef FLR_RESULT_CODES_H 19 | #define FLR_RESULT_CODES_H 20 | 21 | enum _returnCodes { 22 | MAX_ERR_CODE = 0xFFFFFFFF, // 65535 23 | 24 | R_SUCCESS = 0U, // 0x00000000 25 | R_UART_UNSPECIFIED_FAILURE = 1U, // 0x00000001 26 | R_UART_PORT_FAILURE = 2U, // 0x00000002 27 | R_UART_RECEIVE_TIMEOUT = 3U, // 0x00000003 28 | R_UART_PORT_ALREADY_OPEN = 4U, // 0x00000004 29 | 30 | R_SDK_API_UNSPECIFIED_FAILURE = 272U, // 0x00000110 31 | R_SDK_API_NOT_DEFINED = 273U, // 0x00000111 32 | R_SDK_PKG_UNSPECIFIED_FAILURE = 288U, // 0x00000120 33 | R_SDK_PKG_BUFFER_OVERFLOW = 303U, // 0x0000012F 34 | R_SDK_DSPCH_UNSPECIFIED_FAILURE = 304U, // 0x00000130 35 | R_SDK_DSPCH_SEQUENCE_MISMATCH = 305U, // 0x00000131 36 | R_SDK_DSPCH_ID_MISMATCH = 306U, // 0x00000132 37 | R_SDK_DSPCH_MALFORMED_STATUS = 307U, // 0x00000133 38 | R_SDK_TX_UNSPECIFIED_FAILURE = 320U, // 0x00000140 39 | R_CAM_RX_UNSPECIFIED_FAILURE = 336U, // 0x00000150 40 | R_CAM_DSPCH_UNSPECIFIED_FAILURE = 352U, // 0x00000160 41 | R_CAM_DSPCH_BAD_CMD_ID = 353U, // 0x00000161 42 | R_CAM_DSPCH_BAD_PAYLOAD_STATUS = 354U, // 0x00000162 43 | R_CAM_PKG_UNSPECIFIED_FAILURE = 368U, // 0x00000170 44 | R_CAM_PKG_INSUFFICIENT_BYTES = 381U, // 0x0000017D 45 | R_CAM_PKG_EXCESS_BYTES = 382U, // 0x0000017E 46 | R_CAM_PKG_BUFFER_OVERFLOW = 383U, // 0x0000017F 47 | R_CAM_API_UNSPECIFIED_FAILURE = 384U, // 0x00000180 48 | R_CAM_API_INVALID_INPUT = 385U, // 0x00000181 49 | R_CAM_TX_UNSPECIFIED_FAILURE = 400U, // 0x00000190 50 | R_API_RX_UNSPECIFIED_FAILURE = 416U, // 0x000001A0 51 | R_CAM_FEATURE_NOT_ENABLED = 432U, // 0x000001B0 52 | 53 | FLR_OK = 0U, // 0x00000000 54 | FLR_COMM_OK = 0U, // 0x00000000 55 | 56 | FLR_ERROR = 513U, // 0x00000201 57 | FLR_NOT_READY = 514U, // 0x00000202 58 | FLR_RANGE_ERROR = 515U, // 0x00000203 59 | FLR_CHECKSUM_ERROR = 516U, // 0x00000204 60 | FLR_BAD_ARG_POINTER_ERROR = 517U, // 0x00000205 61 | FLR_DATA_SIZE_ERROR = 518U, // 0x00000206 62 | FLR_UNDEFINED_FUNCTION_ERROR = 519U, // 0x00000207 63 | FLR_ILLEGAL_ADDRESS_ERROR = 520U, // 0x00000208 64 | FLR_BAD_OUT_TYPE = 521U, // 0x00000209 65 | FLR_BAD_OUT_INTERFACE = 522U, // 0x0000020A 66 | FLR_COMM_PORT_NOT_OPEN = 613U, // 0x00000265 67 | FLR_COMM_INVALID_PORT_ERROR = 614U, // 0x00000266 68 | FLR_COMM_RANGE_ERROR = 615U, // 0x00000267 69 | FLR_ERROR_CREATING_COMM = 616U, // 0x00000268 70 | FLR_ERROR_STARTING_COMM = 617U, // 0x00000269 71 | FLR_ERROR_CLOSING_COMM = 618U, // 0x0000026A 72 | FLR_COMM_CHECKSUM_ERROR = 619U, // 0x0000026B 73 | FLR_COMM_NO_DEV = 620U, // 0x0000026C 74 | FLR_COMM_TIMEOUT_ERROR = 621U, // 0x0000026D 75 | FLR_COMM_ERROR_WRITING_COMM = 621U, // 0x0000026D 76 | FLR_COMM_ERROR_READING_COMM = 622U, // 0x0000026E 77 | FLR_COMM_COUNT_ERROR = 623U, // 0x0000026F 78 | FLR_OPERATION_CANCELED = 638U, // 0x0000027E 79 | FLR_UNDEFINED_ERROR_CODE = 639U, // 0x0000027F 80 | FLR_LEN_NOT_SUBBLOCK_BOUNDARY = 640U, // 0x00000280 81 | FLR_CONFIG_ERROR = 641U, // 0x00000281 82 | FLR_I2C_ERROR = 642U, // 0x00000282 83 | FLR_CAM_BUSY = 643U, // 0x00000283 84 | FLR_HEATER_ERROR = 644U, // 0x00000284 85 | FLR_WINDOW_ERROR = 645U, // 0x00000285 86 | FLR_VBATT_ERROR = 646U, // 0x00000286 87 | R_SYM_UNSPECIFIED_FAILURE = 768U, // 0x00000300 88 | R_SYM_INVALID_POSITION_ERROR = 769U, // 0x00000301 89 | FLR_RES_NOT_AVAILABLE = 800U, // 0x00000320 90 | FLR_RES_NOT_IMPLEMENTED = 801U, // 0x00000321 91 | FLR_RES_RANGE_ERROR = 802U, // 0x00000322 92 | FLR_SYSTEMINIT_XX_ERROR = 900U, // 0x00000384 93 | FLR_SDIO_XX_ERROR = 1000U, // 0x000003E8 94 | FLR_STOR_SD_XX_ERROR = 1100U, // 0x0000044C 95 | FLR_USB_VIDEO_XX_ERROR = 1200U, // 0x000004B0 96 | FLR_USB_CDC_XX_ERROR = 1300U, // 0x00000514 97 | FLR_USB_MSD_XX_ERROR = 1400U, // 0x00000578 98 | FLR_NET_XX_ERROR = 1500U, // 0x000005DC 99 | FLR_BT_XX_ERROR = 1600U, // 0x00000640 100 | FLR_FLASH_XX_ERROR = 1700U, // 0x000006A4 101 | }; 102 | typedef enum _returnCodes FLR_RESULT; 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/boson/Serializer_BuiltIn.c: -------------------------------------------------------------------------------- 1 | #include "Serializer_BuiltIn.h" 2 | //#include 3 | 4 | void byteToBOOL(const uint8_t *inBuff, _Bool *outVal){ 5 | *outVal = (_Bool) *inBuff; 6 | } 7 | void byteToBOOLArray(const uint8_t *inBuff, _Bool *outVal,uint16_t length){ 8 | uint16_t i; 9 | for (i=0;i>8 & 0xff); 161 | *outPtr = (uint8_t) (tempBytes & 0xff); 162 | } 163 | void INT_16ArrayToByte(const int16_t *inVal,uint16_t length, const uint8_t *outBuff){ 164 | uint16_t i; 165 | for (i=0;i>8 & 0xff); 173 | *outPtr = (uint8_t) (inVal & 0xff); 174 | } 175 | void UINT_16ArrayToByte(const uint16_t *inVal,uint16_t length, const uint8_t *outBuff){ 176 | uint16_t i; 177 | for (i=0;i>24 & 0xff); 186 | *outPtr++ = (uint8_t) (tempBytes>>16 & 0xff); 187 | *outPtr++ = (uint8_t) (tempBytes>>8 & 0xff); 188 | *outPtr = (uint8_t) (tempBytes & 0xff); 189 | } 190 | void INT_32ArrayToByte(const int32_t *inVal,uint16_t length, const uint8_t *outBuff){ 191 | uint16_t i; 192 | for (i=0;i>24 & 0xff); 200 | *outPtr++ = (uint8_t) (inVal>>16 & 0xff); 201 | *outPtr++ = (uint8_t) (inVal>>8 & 0xff); 202 | *outPtr = (uint8_t) (inVal & 0xff); 203 | } 204 | void UINT_32ArrayToByte(const uint32_t *inVal,uint16_t length, const uint8_t *outBuff){ 205 | uint16_t i; 206 | for (i=0;i>24 & 0xff); 215 | *outPtr++ = (uint8_t) (tempBytes>>16 & 0xff); 216 | *outPtr++ = (uint8_t) (tempBytes>>8 & 0xff); 217 | *outPtr = (uint8_t) (tempBytes & 0xff); 218 | } 219 | void FLOATArrayToByte(const float *inVal,uint16_t length, const uint8_t *outBuff){ 220 | uint16_t i; 221 | for (i=0;i>56 & 0xff); 230 | *outPtr++ = (uint8_t) (tempBytes>>48 & 0xff); 231 | *outPtr++ = (uint8_t) (tempBytes>>40 & 0xff); 232 | *outPtr++ = (uint8_t) (tempBytes>>32 & 0xff); 233 | *outPtr++ = (uint8_t) (tempBytes>>24 & 0xff); 234 | *outPtr++ = (uint8_t) (tempBytes>>16 & 0xff); 235 | *outPtr++ = (uint8_t) (tempBytes>>8 & 0xff); 236 | *outPtr = (uint8_t) (tempBytes & 0xff); 237 | } 238 | void DOUBLEArrayToByte(const double *inVal,uint16_t length, const uint8_t *outBuff){ 239 | uint16_t i; 240 | for (i=0;i 5 | 6 | extern void byteToBOOL(const uint8_t*inBuff, _Bool *outVal); 7 | extern void byteToCHAR(const uint8_t*inBuff, int8_t *outVal); 8 | extern void byteToUCHAR(const uint8_t*inBuff, uint8_t *outVal); 9 | extern void byteToINT_16(const uint8_t*inBuff, int16_t *outVal); 10 | extern void byteToUINT_16(const uint8_t*inBuff, uint16_t *outVal); 11 | extern void byteToINT_32(const uint8_t*inBuff, int32_t *outVal); 12 | extern void byteToUINT_32(const uint8_t*inBuff, uint32_t *outVal); 13 | extern void byteToFLOAT(const uint8_t*inBuff, float *outVal); 14 | extern void byteToDOUBLE(const uint8_t*inBuff, double *outVal); 15 | 16 | extern void byteToBOOLArray(const uint8_t*inBuff, _Bool *outVal,uint16_t length); 17 | extern void byteToCHARArray(const uint8_t*inBuff, int8_t *outVal,uint16_t length); 18 | extern void byteToUCHARArray(const uint8_t*inBuff, uint8_t *outVal,uint16_t length); 19 | extern void byteToINT_16Array(const uint8_t*inBuff, int16_t *outVal,uint16_t length); 20 | extern void byteToUINT_16Array(const uint8_t*inBuff, uint16_t *outVal,uint16_t length); 21 | extern void byteToINT_32Array(const uint8_t*inBuff, int32_t *outVal,uint16_t length); 22 | extern void byteToUINT_32Array(const uint8_t*inBuff, uint32_t *outVal,uint16_t length); 23 | extern void byteToFLOATArray(const uint8_t*inBuff, float *outVal,uint16_t length); 24 | extern void byteToDOUBLEArray(const uint8_t*inBuff, double *outVal,uint16_t length); 25 | 26 | extern void BOOLToByte(const _Bool inVal, const uint8_t *outBuff); 27 | extern void CHARToByte(const int8_t inVal, const uint8_t *outBuff ); 28 | extern void UCHARToByte(const uint8_t inVal, const uint8_t *outBuff); 29 | extern void INT_16ToByte(const int16_t inVal, const uint8_t *outBuff); 30 | extern void UINT_16ToByte(const uint16_t inVal, const uint8_t *outBuff); 31 | extern void INT_32ToByte(const int32_t inVal, const uint8_t *outBuff); 32 | extern void UINT_32ToByte(const uint32_t inVal, const uint8_t *outBuff); 33 | extern void FLOATToByte(const float inVal, const uint8_t *outBuff); 34 | extern void DOUBLEToByte(const double inVal, const uint8_t *outBuff); 35 | 36 | extern void BOOLArrayToByte(const _Bool *inVal, uint16_t length, const uint8_t *outBuff); 37 | extern void CHARArrayToByte(const int8_t *inVal, uint16_t length, const uint8_t *outBuff ); 38 | extern void UCHARArrayToByte(const uint8_t *inVal, uint16_t length, const uint8_t *outBuff); 39 | extern void INT_16ArrayToByte(const int16_t *inVal, uint16_t length, const uint8_t *outBuff); 40 | extern void UINT_16ArrayToByte(const uint16_t *inVal, uint16_t length, const uint8_t *outBuff); 41 | extern void INT_32ArrayToByte(const int32_t *inVal, uint16_t length, const uint8_t *outBuff); 42 | extern void UINT_32ArrayToByte(const uint32_t *inVal, uint16_t length, const uint8_t *outBuff); 43 | extern void FLOATArrayToByte(const float *inVal, uint16_t length, const uint8_t *outBuff); 44 | extern void DOUBLEArrayToByte(const double *inVal, uint16_t length, const uint8_t *outBuff); 45 | 46 | 47 | #endif //BOSON_BUILTIN_HANDLERS_H -------------------------------------------------------------------------------- /firmware/DiVA-fw/boson/flirCRC.c: -------------------------------------------------------------------------------- 1 | #include "flirCRC.h" 2 | 3 | static const uint16_t ccitt_16Table[] = { 4 | 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 5 | 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 6 | 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 7 | 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 8 | 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 9 | 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 10 | 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 11 | 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 12 | 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 13 | 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 14 | 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 15 | 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 16 | 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 17 | 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 18 | 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 19 | 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 20 | 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 21 | 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 22 | 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 23 | 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 24 | 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 25 | 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 26 | 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 27 | 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 28 | 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 29 | 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 30 | 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 31 | 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 32 | 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 33 | 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 34 | 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 35 | 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 36 | }; 37 | 38 | 39 | #define _FAST 40 | #ifdef _FAST 41 | // 42 | // ===== ByteCRC16 ===== FAST 43 | // Calculate (update) the CRC16 for a single 8-bit byte 44 | // 45 | int ByteCRC16(int value, int crcin) 46 | { 47 | return (unsigned short)((crcin << 8) ^ ccitt_16Table[((crcin >> 8) ^ (value)) & 255]); 48 | } 49 | 50 | 51 | #else 52 | // 53 | // ===== ByteCRC16 ===== SLOW 54 | // Calculate (update) the CRC16 for a single 8-bit byte 55 | // 56 | int ByteCRC16(int value, int crcin) 57 | { 58 | int k = (((crcin >> 8) ^ value) & 255) << 8; 59 | int crc = 0; 60 | int bits = 8; 61 | do 62 | { 63 | if ( ( crc ^ k ) & 0x8000 ) 64 | crc = (crc << 1) ^ 0x1021; 65 | else 66 | crc <<= 1; 67 | k <<= 1; 68 | } 69 | while ( --bits ); 70 | return ((crcin << 8) ^ crc); 71 | } 72 | #endif 73 | 74 | 75 | #define _LITTLE_ENDIAN 0 76 | #define _BIG_ENDIAN 1 77 | 78 | // 79 | // ===== CalcCRC16Words ===== 80 | // Calculate the CRC for a buffer of 16-bit words. Supports both 81 | // Little and Big Endian formats using conditional compilation. 82 | // Note: minimum count is 1 (0 case not handled) 83 | // 84 | uint16_t calcFlirCRC16Words(unsigned int count, short *buffer) 85 | { 86 | int crc = FLIR_CRC_INITIAL_VALUE; 87 | int endian = _LITTLE_ENDIAN; 88 | do 89 | { 90 | int value = *buffer++; 91 | if ( endian == _BIG_ENDIAN ) 92 | { 93 | crc = ByteCRC16(value >> 8, crc); 94 | crc = ByteCRC16(value, crc); 95 | } 96 | else 97 | { 98 | crc = ByteCRC16(value, crc); 99 | crc = ByteCRC16(value >> 8, crc); 100 | } 101 | } 102 | while ( --count ); 103 | return (uint16_t) crc; 104 | } 105 | 106 | 107 | // 108 | // ===== CalcCRC16Bytes ===== 109 | // Calculate the CRC for a buffer of 8-bit words. 110 | // Note: minimum count is 1 (0 case not handled) 111 | // 112 | uint16_t calcFlirCRC16Bytes(unsigned int count, uint8_t *buffer) 113 | { 114 | 115 | int crc = FLIR_CRC_INITIAL_VALUE; 116 | 117 | do 118 | { 119 | int value = *buffer++; 120 | crc = ByteCRC16(value, crc); 121 | } 122 | while ( --count ); 123 | 124 | return (uint16_t) crc; 125 | } 126 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/boson/flirCRC.h: -------------------------------------------------------------------------------- 1 | #ifndef _FLIR_CRC_H_ 2 | #define _FLIR_CRC_H_ 3 | 4 | #include 5 | 6 | #define FLIR_CRC_INITIAL_VALUE (0x1D0F) 7 | 8 | uint16_t calcFlirCRC16Words(unsigned int count, short *buffer); 9 | uint16_t calcFlirCRC16Bytes(unsigned int count, uint8_t *buffer); 10 | int ByteCRC16(int value, int crcin); 11 | #endif // _FLIR_CRC_H_ 12 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/crc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef CRC16_FAST 3 | static const unsigned int crc16_table[256] = { 4 | 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 5 | 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 6 | 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 7 | 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 8 | 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 9 | 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 10 | 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 11 | 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 12 | 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 13 | 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 14 | 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 15 | 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 16 | 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 17 | 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 18 | 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 19 | 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 20 | 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 21 | 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 22 | 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 23 | 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 24 | 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 25 | 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 26 | 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 27 | 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 28 | 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 29 | 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 30 | 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 31 | 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 32 | 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 33 | 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 34 | 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 35 | 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 36 | }; 37 | 38 | unsigned short crc16(const unsigned char *buffer, int len) 39 | { 40 | return 0; 41 | unsigned short crc; 42 | 43 | crc = 0; 44 | while(len-- > 0) 45 | crc = crc16_table[((crc >> 8) ^ (*buffer++)) & 0xFF] ^ (crc << 8); 46 | 47 | return crc; 48 | } 49 | #else 50 | unsigned short crc16(const unsigned char* data_p, int length) { 51 | unsigned char x; 52 | unsigned short crc = 0; 53 | 54 | while (length--){ 55 | x = crc >> 8 ^ *data_p++; 56 | x ^= x>>4; 57 | crc = (crc << 8) ^ ((unsigned short)(x << 12)) ^ ((unsigned short)(x <<5)) ^ ((unsigned short)x); 58 | } 59 | return crc; 60 | } 61 | #endif -------------------------------------------------------------------------------- /firmware/DiVA-fw/dma.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void start_dma(void){ 5 | reader_reset_write(1); 6 | 7 | reader_transfer_size_write(640*512); 8 | reader_burst_size_write(128); 9 | reader_enable_write(1); 10 | } 11 | 12 | void stop_dma(void){ 13 | reader_reset_write(1); 14 | } -------------------------------------------------------------------------------- /firmware/DiVA-fw/hdmi_edid.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //#include "i2c.h" 4 | 5 | #if 0 6 | 7 | I2C hdmi_out0_i2c; 8 | int hdmi_out0_debug_enabled = 0; 9 | 10 | void hdmi_out0_i2c_init(void) { 11 | printf("hdmi_out0: Init I2C..."); 12 | hdmi_out0_i2c.w_read = hdmi_i2c_w_read; 13 | hdmi_out0_i2c.w_write = hdmi_i2c_w_write; 14 | hdmi_out0_i2c.r_read = hdmi_i2c_r_read; 15 | i2c_init(&hdmi_out0_i2c); 16 | printf("finished.\n"); 17 | } 18 | 19 | void hdmi_out0_print_edid(void) { 20 | int eeprom_addr, e, extension_number = 0; 21 | unsigned char b; 22 | unsigned char sum = 0; 23 | 24 | i2c_start_cond(&hdmi_out0_i2c); 25 | b = i2c_write(&hdmi_out0_i2c, 0xa0); 26 | if (!b && hdmi_out0_debug_enabled) 27 | printf("hdmi_out0: NACK while writing slave address!\n"); 28 | b = i2c_write(&hdmi_out0_i2c, 0x00); 29 | if (!b && hdmi_out0_debug_enabled) 30 | printf("hdmi_out0: NACK while writing eeprom address!\n"); 31 | i2c_start_cond(&hdmi_out0_i2c); 32 | b = i2c_write(&hdmi_out0_i2c, 0xa1); 33 | if (!b && hdmi_out0_debug_enabled) 34 | printf("hdmi_out0: NACK while writing slave address (2)!\n"); 35 | for (eeprom_addr = 0 ; eeprom_addr < 128 ; eeprom_addr++) { 36 | b = i2c_read(&hdmi_out0_i2c, eeprom_addr == 127 && extension_number == 0 ? 0 : 1); 37 | sum +=b; 38 | printf("%02X ", b); 39 | if(!((eeprom_addr+1) % 16)) 40 | printf("\n"); 41 | if(eeprom_addr == 126) 42 | extension_number = b; 43 | if(eeprom_addr == 127 && sum != 0) 44 | { 45 | printf("Checksum ERROR in EDID block 0\n"); 46 | i2c_stop_cond(&hdmi_out0_i2c); 47 | return; 48 | } 49 | } 50 | for(e = 0; e < extension_number; e++) 51 | { 52 | printf("\n"); 53 | sum = 0; 54 | for (eeprom_addr = 0 ; eeprom_addr < 128 ; eeprom_addr++) { 55 | b = i2c_read(&hdmi_out0_i2c, eeprom_addr == 127 && e == extension_number - 1 ? 0 : 1); 56 | sum += b; 57 | printf("%02X ", b); 58 | if(!((eeprom_addr+1) % 16)) 59 | printf("\n"); 60 | if(eeprom_addr == 127 && sum != 0) 61 | { 62 | printf("Checksum ERROR in EDID extension block %d\n", e); 63 | i2c_stop_cond(&hdmi_out0_i2c); 64 | return; 65 | } 66 | } 67 | } 68 | i2c_stop_cond(&hdmi_out0_i2c); 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/hyperram.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "include/time.h" 5 | #include "include/hyperram.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | 13 | #ifdef CSR_HYPERRAM_BASE 14 | 15 | /* Prototypes */ 16 | 17 | void set_io_delay(int); 18 | void set_clk_delay(int); 19 | static int basic_memtest(void); 20 | 21 | 22 | void set_io_delay(int cnt){ 23 | hyperram_io_loadn_write(0); 24 | hyperram_io_loadn_write(1); 25 | hyperram_io_direction_write(0); 26 | 27 | /* 25ps of delay per tap. 28 | Each rising edge adds to the io delay */ 29 | for(int i = 0; i < cnt; i++){ 30 | hyperram_io_move_write(1); 31 | hyperram_io_move_write(0); 32 | } 33 | } 34 | 35 | void set_clk_delay(int cnt){ 36 | hyperram_clk_loadn_write(0); 37 | hyperram_clk_loadn_write(1); 38 | hyperram_clk_direction_write(0); 39 | 40 | /* 25ps of delay per tap. 41 | Each rising edge adds to the io delay */ 42 | for(int i = 0; i < cnt; i++){ 43 | hyperram_clk_move_write(1); 44 | hyperram_clk_move_write(0); 45 | } 46 | } 47 | 48 | 49 | 50 | /* 51 | Test memory location by writing a value and attempting read-back. 52 | Try twice to avoid situation where memory is read-only and set from a previous test. 53 | */ 54 | static int basic_memtest(void){ 55 | 56 | *((volatile uint32_t*)HYPERRAM_BASE) = 0xFF55AACD; 57 | if(*((volatile uint32_t*)HYPERRAM_BASE) != 0xFF55AACD) 58 | return 0; 59 | // 60 | *((volatile uint32_t*)HYPERRAM_BASE) = 0xA3112233; 61 | if(*((volatile uint32_t*)HYPERRAM_BASE) != 0xA3112233) 62 | return 0; 63 | 64 | return 1; 65 | } 66 | 67 | 68 | void hyperram_init(void){ 69 | int window = 0; 70 | int clk_del = 0; 71 | int io_del = 0; 72 | 73 | while(clk_del < 128){ 74 | set_clk_delay(clk_del >> 2); 75 | set_io_delay(io_del); 76 | int i = 0; 77 | printf("%u,%u, %u |", clk_del >> 2, clk_del & 1 ? 1 : 0, clk_del & 2 ? 1 : 0); 78 | for(i = 0; i < 64; i++){ 79 | 80 | int pass = basic_memtest(); 81 | 82 | // Shift our PLL 83 | crg_phase_sel_write(0); 84 | crg_phase_dir_write(0); 85 | crg_phase_step_write(0); 86 | crg_phase_step_write(1); 87 | 88 | if(i & 1) 89 | printf("%c", pass > 0 ? '0' : '-'); 90 | 91 | if(pass == 1){ 92 | window++; 93 | } 94 | else if(pass != 1){ 95 | if(window >= 6){ 96 | break; 97 | }else { 98 | window = 0; 99 | } 100 | } 101 | 102 | } 103 | printf("| %d \r", window ); 104 | if(window >= 5){ 105 | for(i = 0; i < window/2; i++){ 106 | // Shift our PLL up 107 | crg_phase_sel_write(0); 108 | crg_phase_dir_write(1); 109 | crg_phase_step_write(0); 110 | crg_phase_step_write(1); 111 | } 112 | return; 113 | } 114 | window = 0; 115 | clk_del = (clk_del + 1); 116 | 117 | crg_slip_hr2x90_write(clk_del & 1 ? 1 : 0); 118 | crg_slip_hr2x_write(clk_del & 2 ? 1 : 0); 119 | 120 | crg_slip_hr2x90_write(0); 121 | crg_slip_hr2x_write(0); 122 | } 123 | 124 | printf("\n\n Error: RAM Init failed :(\n Restarting in... "); 125 | for(int i = 0; i < 5; i++){ 126 | msleep(1000); 127 | printf("\b%u",5-i); 128 | } 129 | 130 | while(1){ 131 | reboot_ctrl_write(0xac); 132 | } 133 | 134 | } 135 | 136 | 137 | 138 | void prbs_memtest(uint32_t base, uint32_t len){ 139 | uint32_t start; 140 | uint32_t end; 141 | 142 | /* init timer */ 143 | timer0_en_write(0); 144 | timer0_reload_write(0); 145 | timer0_load_write(0xffffffff); 146 | timer0_en_write(1); 147 | 148 | uint32_t burst = 320; 149 | 150 | reader_source_mux_write(1); 151 | writer_sink_mux_write(1); 152 | 153 | prbs_source_reset_write(1); 154 | 155 | reader_reset_write(1); 156 | reader_burst_size_write(burst); 157 | reader_transfer_size_write(len/4); 158 | // Currently adr is handled by the buffers module 159 | //reader_start_address_write(base>>2); 160 | 161 | 162 | /* write speed */ 163 | timer0_update_value_write(1); 164 | start = timer0_value_read(); 165 | 166 | reader_enable_write(1); 167 | while(reader_done_read() == 0); 168 | 169 | timer0_update_value_write(1); 170 | end = timer0_value_read(); 171 | 172 | uint32_t rate = (CONFIG_CLOCK_FREQUENCY*10)/((start-end)/8); 173 | printf("Write Speed: %u.%u MBytes/s ( %u cycles )\n", rate / 10, rate % 10, start-end); 174 | 175 | writer_reset_write(1); 176 | writer_burst_size_write(burst); 177 | writer_transfer_size_write(len/4); 178 | // Currently adr is handled by the buffers module 179 | //writer_start_address_write(base>>2); 180 | 181 | prbs_sink_reset_write(1); 182 | 183 | 184 | /* write speed */ 185 | timer0_update_value_write(1); 186 | start = timer0_value_read(); 187 | 188 | writer_enable_write(1); 189 | while(writer_done_read() == 0); 190 | 191 | timer0_update_value_write(1); 192 | end = timer0_value_read(); 193 | 194 | rate = (CONFIG_CLOCK_FREQUENCY*10)/((start-end)/8); 195 | printf("Read Speed: %u.%u MBytes/s ( %u cycles )\n", rate / 10, rate % 10, start-end); 196 | 197 | printf("Memtest: "); 198 | if(prbs_sink_good_read() == writer_transfer_size_read()){ 199 | printf("%uKB ( 0x%x )...OK\n", 4*prbs_sink_good_read()/1024, 4*prbs_sink_good_read()); 200 | }else { 201 | printf("Not OK. :(\n Good: 0x%x (%uKB) [%x], Bad: 0x%x\n", prbs_sink_good_read(),4*prbs_sink_good_read()/1024, writer_transfer_size_read(),prbs_sink_bad_read()); 202 | } 203 | 204 | 205 | reader_external_sync_write(1); 206 | writer_external_sync_write(1); 207 | 208 | writer_enable_write(0); 209 | reader_enable_write(0); 210 | 211 | reader_source_mux_write(0); 212 | writer_sink_mux_write(0); 213 | 214 | } 215 | 216 | 217 | 218 | #else 219 | 220 | 221 | void hyperram_init(){ 222 | return; 223 | } 224 | 225 | #endif 226 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/i2c.c: -------------------------------------------------------------------------------- 1 | // This file is Copyright (c) 2020 Antmicro 2 | #include 3 | #include 4 | 5 | #ifdef CSR_I2C0_BASE 6 | 7 | #define I2C_PERIOD_CYCLES (CONFIG_CLOCK_FREQUENCY / I2C_FREQ_HZ) 8 | #define I2C_DELAY(n) cdelay((n)*I2C_PERIOD_CYCLES/4) 9 | 10 | static inline void cdelay(int i) 11 | { 12 | while(i > 0) { 13 | __asm__ volatile(CONFIG_CPU_NOP); 14 | i--; 15 | } 16 | } 17 | 18 | static inline void i2c_oe_scl_sda(bool oe, bool scl, bool sda) 19 | { 20 | i2c0_w_write( 21 | ((oe & 1) << CSR_I2C0_W_OE_OFFSET) | 22 | ((scl & 1) << CSR_I2C0_W_SCL_OFFSET) | 23 | ((sda & 1) << CSR_I2C0_W_SDA_OFFSET) 24 | ); 25 | } 26 | 27 | // START condition: 1-to-0 transition of SDA when SCL is 1 28 | static void i2c_start(void) 29 | { 30 | i2c_oe_scl_sda(1, 1, 1); 31 | I2C_DELAY(1); 32 | i2c_oe_scl_sda(1, 1, 0); 33 | I2C_DELAY(1); 34 | i2c_oe_scl_sda(1, 0, 0); 35 | I2C_DELAY(1); 36 | } 37 | 38 | // STOP condition: 0-to-1 transition of SDA when SCL is 1 39 | static void i2c_stop(void) 40 | { 41 | i2c_oe_scl_sda(1, 0, 0); 42 | I2C_DELAY(1); 43 | i2c_oe_scl_sda(1, 1, 0); 44 | I2C_DELAY(1); 45 | i2c_oe_scl_sda(1, 1, 1); 46 | I2C_DELAY(1); 47 | i2c_oe_scl_sda(0, 1, 1); 48 | } 49 | 50 | // Call when in the middle of SCL low, advances one clk period 51 | static void i2c_transmit_bit(int value) 52 | { 53 | i2c_oe_scl_sda(1, 0, value); 54 | I2C_DELAY(1); 55 | i2c_oe_scl_sda(1, 1, value); 56 | I2C_DELAY(2); 57 | i2c_oe_scl_sda(1, 0, value); 58 | I2C_DELAY(1); 59 | i2c_oe_scl_sda(0, 0, 0); // release line 60 | } 61 | 62 | // Call when in the middle of SCL low, advances one clk period 63 | static int i2c_receive_bit(void) 64 | { 65 | int value; 66 | i2c_oe_scl_sda(0, 0, 0); 67 | I2C_DELAY(1); 68 | i2c_oe_scl_sda(0, 1, 0); 69 | I2C_DELAY(1); 70 | // read in the middle of SCL high 71 | value = i2c0_r_read() & 1; 72 | I2C_DELAY(1); 73 | i2c_oe_scl_sda(0, 0, 0); 74 | I2C_DELAY(1); 75 | return value; 76 | } 77 | 78 | // Send data byte and return 1 if slave sends ACK 79 | static bool i2c_transmit_byte(unsigned char data) 80 | { 81 | int i; 82 | int ack; 83 | 84 | // SCL should have already been low for 1/4 cycle 85 | i2c_oe_scl_sda(0, 0, 0); 86 | for (i = 0; i < 8; ++i) { 87 | // MSB first 88 | i2c_transmit_bit((data & (1 << 7)) != 0); 89 | data <<= 1; 90 | } 91 | ack = i2c_receive_bit(); 92 | 93 | // 0 from slave means ack 94 | return ack == 0; 95 | } 96 | 97 | // Read data byte and send ACK if ack=1 98 | static unsigned char i2c_receive_byte(bool ack) 99 | { 100 | int i; 101 | unsigned char data = 0; 102 | 103 | for (i = 0; i < 8; ++i) { 104 | data <<= 1; 105 | data |= i2c_receive_bit(); 106 | } 107 | i2c_transmit_bit(!ack); 108 | 109 | return data; 110 | } 111 | 112 | // Reset line state 113 | void i2c_reset(void) 114 | { 115 | int i; 116 | i2c_oe_scl_sda(1, 1, 1); 117 | I2C_DELAY(8); 118 | for (i = 0; i < 9; ++i) { 119 | i2c_oe_scl_sda(1, 0, 1); 120 | I2C_DELAY(2); 121 | i2c_oe_scl_sda(1, 1, 1); 122 | I2C_DELAY(2); 123 | } 124 | i2c_oe_scl_sda(0, 0, 1); 125 | I2C_DELAY(1); 126 | i2c_stop(); 127 | i2c_oe_scl_sda(0, 1, 1); 128 | I2C_DELAY(8); 129 | } 130 | 131 | /* 132 | * Read slave memory over I2C starting at given address 133 | * 134 | * First writes the memory starting address, then reads the data: 135 | * START WR(slaveaddr) WR(addr) STOP START WR(slaveaddr) RD(data) RD(data) ... STOP 136 | * Some chips require that after transmiting the address, there will be no STOP in between: 137 | * START WR(slaveaddr) WR(addr) START WR(slaveaddr) RD(data) RD(data) ... STOP 138 | */ 139 | bool i2c_read(unsigned char slave_addr, unsigned char addr, unsigned char *data, unsigned int len, bool send_stop) 140 | { 141 | int i; 142 | 143 | i2c_start(); 144 | 145 | if(!i2c_transmit_byte(I2C_ADDR_WR(slave_addr))) { 146 | i2c_stop(); 147 | return false; 148 | } 149 | if(!i2c_transmit_byte(0)) { 150 | i2c_stop(); 151 | return false; 152 | } 153 | 154 | if(!i2c_transmit_byte(addr)) { 155 | i2c_stop(); 156 | return false; 157 | } 158 | 159 | if (send_stop) { 160 | i2c_stop(); 161 | } 162 | i2c_start(); 163 | 164 | if(!i2c_transmit_byte(I2C_ADDR_RD(slave_addr))) { 165 | i2c_stop(); 166 | return false; 167 | } 168 | for (i = 0; i < len; ++i) { 169 | data[i] = i2c_receive_byte(i != len - 1); 170 | } 171 | 172 | i2c_stop(); 173 | 174 | return true; 175 | } 176 | 177 | /* 178 | * Write slave memory over I2C starting at given address 179 | * 180 | * First writes the memory starting address, then writes the data: 181 | * START WR(slaveaddr) WR(addr) WR(data) WR(data) ... STOP 182 | */ 183 | bool i2c_write(unsigned char slave_addr, unsigned char addr, const unsigned char *data, unsigned int len) 184 | { 185 | int i; 186 | 187 | i2c_start(); 188 | 189 | if(!i2c_transmit_byte(I2C_ADDR_WR(slave_addr))) { 190 | i2c_stop(); 191 | return false; 192 | } 193 | if(!i2c_transmit_byte(0)) { 194 | i2c_stop(); 195 | return false; 196 | } 197 | if(!i2c_transmit_byte(addr)) { 198 | i2c_stop(); 199 | return false; 200 | } 201 | for (i = 0; i < len; ++i) { 202 | if(!i2c_transmit_byte(data[i])) { 203 | i2c_stop(); 204 | return false; 205 | } 206 | } 207 | 208 | i2c_stop(); 209 | 210 | return true; 211 | } 212 | 213 | #endif /* CSR_I2C0_BASE */ 214 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/include/boson.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "../boson/FunctionCodes.h" 5 | #include "../boson/Serializer_BuiltIn.h" 6 | #include "../boson/ReturnCodes.h" 7 | #include "../boson/flirCRC.h" 8 | #include "../boson/FSLP.h" 9 | 10 | 11 | void boson_init(void); 12 | FLR_RESULT dispatcher_tx(uint32_t seqNum, FLR_FUNCTION fnID, const uint8_t *sendData, const uint32_t sendBytes); 13 | FLR_RESULT dispatcher_rx(uint8_t* recvData, uint32_t* recvBytes); 14 | FLR_RESULT dispatcher(FLR_FUNCTION fnID, const uint8_t *sendData, const uint32_t sendBytes); 15 | void boson_set_lut(uint32_t lut); 16 | void boson_set_averager(uint32_t en); -------------------------------------------------------------------------------- /firmware/DiVA-fw/include/crc.h: -------------------------------------------------------------------------------- 1 | #ifndef __CRC_H 2 | #define __CRC_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | unsigned short crc16(const unsigned char *buffer, int len); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/include/dma.h: -------------------------------------------------------------------------------- 1 | #ifndef DMA_H__ 2 | #define DMA_H__ 3 | 4 | void start_dma(void); 5 | void stop_dma(void); 6 | 7 | 8 | #endif -------------------------------------------------------------------------------- /firmware/DiVA-fw/include/hdmi_edid.h: -------------------------------------------------------------------------------- 1 | void hdmi_out0_i2c_init(void); 2 | void hdmi_out0_print_edid(void); -------------------------------------------------------------------------------- /firmware/DiVA-fw/include/hyperram.h: -------------------------------------------------------------------------------- 1 | #ifndef HYPERRAM_H__ 2 | #define HYPERRAM_H__ 3 | #include 4 | 5 | void hyperram_init(void); 6 | void prbs_memtest(uint32_t base, uint32_t len); 7 | 8 | #endif -------------------------------------------------------------------------------- /firmware/DiVA-fw/include/i2c.h: -------------------------------------------------------------------------------- 1 | #ifndef __I2C_H 2 | #define __I2C_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | /* I2C frequency defaults to a safe value in range 10-100 kHz to be compatible with SMBus */ 11 | #ifndef I2C_FREQ_HZ 12 | #define I2C_FREQ_HZ 50000 13 | #endif 14 | 15 | #define I2C_ADDR_WR(addr) ((addr) << 1) 16 | #define I2C_ADDR_RD(addr) (((addr) << 1) | 1u) 17 | 18 | void i2c_reset(void); 19 | bool i2c_write(unsigned char slave_addr, unsigned char addr, const unsigned char *data, unsigned int len); 20 | bool i2c_read(unsigned char slave_addr, unsigned char addr, unsigned char *data, unsigned int len, bool send_stop); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #endif /* __I2C_H */ 27 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/include/scaler.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALER_H__ 2 | #define SCALER_H__ 3 | 4 | 5 | void switch_mode(int); 6 | 7 | #endif -------------------------------------------------------------------------------- /firmware/DiVA-fw/include/settings.h: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGS_H__ 2 | #define SETTINGS_H__ 3 | 4 | #include 5 | #include 6 | #include "terminal_menu.h" 7 | 8 | typedef struct { 9 | uint16_t firmware_hash; 10 | uint16_t settings_crc; 11 | 12 | uint8_t pallete; 13 | uint8_t averager; 14 | uint8_t frame_info; 15 | uint8_t debug_info_overlay; 16 | uint8_t frame_info_overlay; 17 | 18 | uint8_t scaler_enable; 19 | 20 | } settings_t; 21 | 22 | settings_t _settings; 23 | 24 | 25 | typedef struct menu_item menu_item_t; 26 | 27 | typedef struct menu_item { 28 | const char* name; 29 | const char* (*value)(const menu_item_t*); 30 | void (*act)(menu_item_t*, event_t); 31 | void (*on_change)(menu_item_t*); 32 | void* pdata; 33 | int value_min; 34 | int value_max; 35 | } menu_item_t; 36 | 37 | 38 | const menu_item_t* setting_menu_items[4]; 39 | 40 | 41 | void init_settings(bool load_defaults); 42 | 43 | const char* palette_value(const menu_item_t* p); 44 | const char* scaler_value(const menu_item_t* p); 45 | const char* enabled_disabled_value(const menu_item_t* p); 46 | const char* boolean_value(const menu_item_t* p); 47 | void basic_integer(menu_item_t* p, event_t e); 48 | 49 | void boson_set_lut(uint32_t lut); 50 | 51 | void boson_palette_changed(const menu_item_t* p); 52 | void _boson_scaler_change(const menu_item_t* p); 53 | void boson_averager_changed(const menu_item_t* p); 54 | 55 | void boson_frame_info_overlay(const menu_item_t* p); 56 | void boson_debug_info_overlay(const menu_item_t* p); 57 | void init_settings(bool load_defaults); 58 | int validate(settings_t* s); 59 | void settings_save(void); 60 | void create_hashes(void); 61 | 62 | 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/include/terminal.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | typedef enum { 5 | TERMINAL_BLACK, 6 | TERMINAL_BLUE, 7 | TERMINAL_GREEN, 8 | TERMINAL_CYAN, 9 | TERMINAL_RED, 10 | TERMINAL_TRANSPARENT, // Magenta used as see-thru colour. 11 | TERMINAL_BRAWN, 12 | TERMINAL_LIGHT_GREY, 13 | TERMINAL_DARK_GREY, 14 | TERMINAL_LIGHT_BLUE, 15 | TERMINAL_LIGHT_GREEN, 16 | TERMINAL_LIGHT_CYAN, 17 | TERMINAL_LIGHT_RED, 18 | TERMINAL_LIGHT_MAGENTA, 19 | TERMINAL_YELLOW, 20 | TERMINAL_WHITE, 21 | } colours_t; 22 | 23 | 24 | void terminal_write_xy(uint32_t x, uint32_t y, uint8_t c, uint8_t colour); 25 | void terminal_write(uint8_t c); 26 | void terminal_set_cursor(uint8_t x, uint8_t y); 27 | 28 | void terminal_set_fg(colours_t fg); 29 | void terminal_set_bg(colours_t bg); 30 | 31 | void terminal_clear(void); 32 | 33 | void terminal_fill(uint8_t x, uint8_t y, uint8_t w, uint8_t h); 34 | void treminal_draw_box(uint8_t x, uint8_t y, uint8_t w, uint8_t h); 35 | void treminal_box_add_hline(uint8_t x, uint8_t y, uint8_t w); 36 | void treminal_box_add_vline(uint8_t x, uint8_t y, uint8_t h); 37 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/include/terminal_menu.h: -------------------------------------------------------------------------------- 1 | #ifndef TERMINAL_MENU_H__ 2 | #define TERMINAL_MENU_H__ 3 | 4 | typedef enum { 5 | BUTTON_A_HOLD = 1, 6 | BUTTON_A_PRESS = 2, 7 | BUTTON_B_HOLD = 4, 8 | BUTTON_B_PRESS = 8 9 | } event_t; 10 | 11 | 12 | void menu_act(event_t); 13 | 14 | #endif -------------------------------------------------------------------------------- /firmware/DiVA-fw/include/time.h: -------------------------------------------------------------------------------- 1 | #ifndef TIME_H__ 2 | #define TIME_H__ 3 | 4 | void msleep(int); 5 | 6 | #endif -------------------------------------------------------------------------------- /firmware/DiVA-fw/linker.ld: -------------------------------------------------------------------------------- 1 | INCLUDE output_format.ld 2 | ENTRY(_start) 3 | 4 | __DYNAMIC = 0; 5 | 6 | INCLUDE regions.ld 7 | 8 | SECTIONS 9 | { 10 | .text : 11 | { 12 | _ftext = .; 13 | *(.text.start) 14 | *(.text .stub .text.* .gnu.linkonce.t.*) 15 | _etext = .; 16 | } > rom 17 | 18 | .rodata : 19 | { 20 | . = ALIGN(4); 21 | _frodata = .; 22 | *(.rodata .rodata.* .gnu.linkonce.r.*) 23 | *(.rodata1) 24 | *(.srodata) 25 | _erodata = .; 26 | } > rom 27 | 28 | .data : AT (ADDR(.rodata) + SIZEOF (.rodata)) 29 | { 30 | . = ALIGN(4); 31 | _fdata = .; 32 | *(.data .data.* .gnu.linkonce.d.*) 33 | *(.data1) 34 | _gp = ALIGN(16); 35 | *(.sdata .sdata.* .gnu.linkonce.s.* .sdata2 .sdata2.*) 36 | _edata = ALIGN(16); /* Make sure _edata is >= _gp. */ 37 | } > sram 38 | 39 | .bss : 40 | { 41 | . = ALIGN(4); 42 | _fbss = .; 43 | *(.dynsbss) 44 | *(.sbss .sbss.* .gnu.linkonce.sb.*) 45 | *(.scommon) 46 | *(.dynbss) 47 | *(.bss .bss.* .gnu.linkonce.b.*) 48 | *(COMMON) 49 | . = ALIGN(4); 50 | _ebss = .; 51 | _end = .; 52 | } > sram 53 | } 54 | 55 | PROVIDE(_fstack = ORIGIN(sram) + LENGTH(sram) - 4); 56 | 57 | PROVIDE(_fdata_rom = LOADADDR(.data)); 58 | PROVIDE(_edata_rom = LOADADDR(.data) + SIZEOF(.data)); -------------------------------------------------------------------------------- /firmware/DiVA-fw/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include "include/hyperram.h" 10 | #include "include/settings.h" 11 | #include "include/time.h" 12 | #include "include/terminal.h" 13 | #include "include/boson.h" 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "include/terminal_menu.h" 22 | 23 | /* prototypes */ 24 | void isr(void); 25 | 26 | 27 | void isr(void){ 28 | 29 | } 30 | 31 | 32 | 33 | int main(int i, char **c) 34 | { 35 | 36 | 37 | console_set_write_hook((console_write_hook)terminal_write); 38 | 39 | terminal_enable_write(1); 40 | 41 | //rgb_div_m_write(400000); 42 | //rgb_config_write(2); 43 | 44 | 45 | terminal_set_bg(TERMINAL_TRANSPARENT); 46 | terminal_set_fg(TERMINAL_TRANSPARENT); 47 | terminal_clear(); 48 | 49 | 50 | //terminal_set_fg(TERMINAL_CYAN); 51 | printf(" ______ ___ __ __ _______ \n"); 52 | printf(" | | |___| | | | | | _ |\n"); 53 | printf(" | _ | ___ | |_| | | |_| |\n"); 54 | printf(" | | | | | | | | | |\n"); 55 | printf(" | |_| | | | | | | |\n"); 56 | printf(" | | | | | | | _ |\n"); 57 | printf(" |______| |___| |___| |__| |__|\n"); 58 | 59 | 60 | //terminal_set_fg(TERMINAL_YELLOW); 61 | printf(" - Digital Video Interface for Boson -\n"); 62 | //terminal_set_fg(TERMINAL_CYAN); 63 | 64 | printf("\n (c) Copyright 2019-2020 GetLabs \n"); 65 | printf(" fw built: "__DATE__ " " __TIME__ " \n\n"); 66 | 67 | printf(" Firmware git sha1: "DIVA_GIT_SHA1"\n"); 68 | printf(" Migen git sha1: "MIGEN_GIT_SHA1"\n"); 69 | printf(" LiteX git sha1: "LITEX_GIT_SHA1"\n"); 70 | 71 | printf("--==========-- \e[1mHyperRAM Init\e[0m ===========--\n"); 72 | 73 | 74 | /* On power up we need these to be set to 0 so that 75 | * PRBS memtest still works */ 76 | buffers_adr0_write(0x0); 77 | buffers_adr1_write(0x0); 78 | buffers_adr2_write(0x0); 79 | 80 | hyperram_init(); 81 | printf("\n"); 82 | prbs_memtest(HYPERRAM_BASE, HYPERRAM_SIZE); 83 | 84 | 85 | terminal_set_bg(TERMINAL_TRANSPARENT); 86 | 87 | bool load_defaults = false; 88 | if(button_raw_read() & 0x00000002){ 89 | load_defaults = true; 90 | } 91 | init_settings(load_defaults); 92 | 93 | //terminal_clear(); 94 | terminal_set_bg(TERMINAL_BLACK); 95 | 96 | 97 | /* Boson Init */ 98 | boson_init(); 99 | 100 | terminal_set_cursor(0,20); 101 | 102 | /* Configure frame buffers, 103 | * each frame currently takes up 0x140000 bytes in RAM */ 104 | buffers_adr0_write(0x000000); 105 | buffers_adr1_write(0x180000); 106 | buffers_adr2_write(0x300000); 107 | 108 | switch_mode(_settings.scaler_enable); 109 | 110 | reader_reset_write(1); 111 | reader_transfer_size_write(640*512); 112 | reader_burst_size_write(128); 113 | reader_enable_write(1); 114 | 115 | writer_reset_write(1); 116 | writer_transfer_size_write(640*512); 117 | writer_burst_size_write(128); 118 | writer_enable_write(1); 119 | 120 | bool debug_window_open = false; 121 | 122 | while(1) { 123 | 124 | /* Print debug window */ 125 | if(_settings.debug_info_overlay || debug_window_open){ 126 | /* Draw the main settings window */ 127 | const uint32_t width = 48; 128 | const uint32_t height = 5; 129 | uint32_t window_x = 50; 130 | uint32_t window_y = 1; 131 | 132 | if(debug_window_open && !_settings.debug_info_overlay) 133 | { 134 | /* Close window */ 135 | terminal_set_fg(TERMINAL_TRANSPARENT); 136 | terminal_set_bg(TERMINAL_TRANSPARENT); 137 | terminal_fill(window_x, window_y, width+1, height+1); 138 | 139 | }else if(!debug_window_open && _settings.debug_info_overlay){ 140 | /* Open window */ 141 | treminal_draw_box(window_x, window_y, width, height); 142 | }else{ 143 | 144 | terminal_set_fg(TERMINAL_WHITE); 145 | terminal_set_bg(TERMINAL_BLUE); 146 | 147 | terminal_set_cursor(++window_x, window_y++); 148 | printf("Boson Interface debug info:"); 149 | 150 | terminal_set_cursor(++window_x, window_y++); 151 | printf("Clk Freq: %u Hz", video_debug_freq_value_read()); 152 | 153 | video_debug_latch_write(1); 154 | terminal_set_cursor(window_x, window_y++); 155 | printf("VSync: %8u | LOW %8u HIGH %8u", video_debug_vsync_low_read() + video_debug_vsync_high_read(), video_debug_vsync_low_read(), video_debug_vsync_high_read()); 156 | terminal_set_cursor(window_x, window_y++); 157 | printf("HSync: %8u | LOW %8u HIGH %8u", video_debug_hsync_low_read() + video_debug_hsync_high_read(), video_debug_hsync_low_read(), video_debug_hsync_high_read()); 158 | terminal_set_cursor(window_x, window_y++); 159 | printf("Lines: %8u ", video_debug_lines_read()); 160 | } 161 | 162 | debug_window_open = _settings.debug_info_overlay; 163 | } 164 | 165 | 166 | msleep(2); 167 | 168 | /* Collect button events and pass them to the menu state machine */ 169 | event_t e = 0; 170 | uint32_t b = button_events_read(); 171 | 172 | if(b & 0x00000002){ 173 | e |= BUTTON_B_PRESS; 174 | } 175 | if(b & 0x00000001){ 176 | e |= BUTTON_A_PRESS; 177 | } 178 | if(b & 0x00000008){ 179 | e |= BUTTON_B_HOLD; 180 | } 181 | if(b & 0x00000004){ 182 | e |= BUTTON_A_HOLD; 183 | } 184 | 185 | if(e){ 186 | menu_act(e); 187 | } 188 | 189 | } 190 | 191 | return 0; 192 | } 193 | 194 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/scaler.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | void init_scaler_640x512_upto_800x600(void); 6 | 7 | 8 | // Scaler coefficients generated with the following parameters: 9 | // delta_offset=0.1466666666666666 10 | // n_phases=75 11 | // n_taps=4 12 | 13 | const uint32_t scaler_coef_64upto75 [300] = { 14 | 0x00000000, 0x01000100, 0x02000000, 0x03000000, 0x0001fff3, 0x010100f3, 0x0201001c, 0x0301fffe, 0x0002ffee, 0x010200d2, 0x02020047, 0x0302fff9, 0x0003ffef, 0x010300a4, 0x0203007a, 0x0303fff3, 15 | 0x0004fff4, 0x01040071, 0x020400ad, 0x0304ffee, 0x8005fffa, 0x8105003f, 0x820500d9, 0x8305ffee, 0x0006ffff, 0x01060016, 0x020600f7, 0x0306fff5, 0x0007fffd, 0x010700ff, 0x02070003, 0x03070000, 16 | 0x0008fff1, 0x010800ee, 0x02080023, 0x0308fffd, 0x0009ffee, 0x010900cb, 0x02090050, 0x0309fff8, 0x000afff0, 0x010a009b, 0x020a0084, 0x030afff2, 0x000bfff5, 0x010b0067, 0x020b00b6, 0x030bffee, 17 | 0x800cfffb, 0x810c0036, 0x820c00e0, 0x830cffef, 0x000dffff, 0x010d0010, 0x020d00fa, 0x030dfff7, 0x000efffa, 0x010e00fe, 0x020e0008, 0x030e0000, 0x000ffff0, 0x010f00e9, 0x020f002b, 0x030ffffc, 18 | 0x0010ffee, 0x011000c3, 0x02100059, 0x0310fff6, 0x0011fff0, 0x01110092, 0x0211008d, 0x0311fff1, 0x0012fff6, 0x0112005e, 0x021200be, 0x0312ffee, 0x8013fffc, 0x8113002e, 0x821300e6, 0x8313fff0, 19 | 0x00140000, 0x0114000a, 0x021400fd, 0x0314fff9, 0x0015fff8, 0x011500fc, 0x0215000d, 0x03150000, 0x0016ffef, 0x011600e3, 0x02160032, 0x0316fffb, 0x0017ffee, 0x011700ba, 0x02170063, 0x0317fff5, 20 | 0x0018fff1, 0x01180088, 0x02180097, 0x0318fff0, 0x0019fff7, 0x01190055, 0x021900c7, 0x0319ffee, 0x801afffd, 0x811a0027, 0x821a00ec, 0x831afff1, 0x001b0000, 0x011b0005, 0x021b00ff, 0x031bfffc, 21 | 0x001cfff6, 0x011c00f9, 0x021c0013, 0x031cffff, 0x001dffee, 0x011d00dd, 0x021d003b, 0x031dfffa, 0x001effee, 0x011e00b2, 0x021e006c, 0x031efff4, 0x001ffff2, 0x011f007f, 0x021f00a0, 0x031fffef, 22 | 0x0020fff8, 0x0120004c, 0x022000ce, 0x0320ffee, 0x8021fffe, 0x81210020, 0x822100f1, 0x8321fff2, 0x00220000, 0x01220001, 0x022200ff, 0x0322ffff, 0x0023fff4, 0x012300f5, 0x02230019, 0x0323ffff, 23 | 0x0024ffee, 0x012400d6, 0x02240043, 0x0324fff9, 0x0025ffef, 0x012500a9, 0x02250075, 0x0325fff3, 0x0026fff3, 0x01260075, 0x022600a9, 0x0326ffef, 0x8027fff9, 0x81270043, 0x822700d6, 0x8327ffee, 24 | 0x0028ffff, 0x01280019, 0x022800f5, 0x0328fff4, 0x0029ffff, 0x012900ff, 0x02290001, 0x03290000, 0x002afff2, 0x012a00f1, 0x022a0020, 0x032afffe, 0x002bffee, 0x012b00ce, 0x022b004c, 0x032bfff8, 25 | 0x002cffef, 0x012c00a0, 0x022c007f, 0x032cfff2, 0x002dfff4, 0x012d006c, 0x022d00b2, 0x032dffee, 0x802efffa, 0x812e003b, 0x822e00dd, 0x832effee, 0x002fffff, 0x012f0013, 0x022f00f9, 0x032ffff6, 26 | 0x0030fffc, 0x013000ff, 0x02300005, 0x03300000, 0x0031fff1, 0x013100ec, 0x02310027, 0x0331fffd, 0x0032ffee, 0x013200c7, 0x02320055, 0x0332fff7, 0x0033fff0, 0x01330097, 0x02330088, 0x0333fff1, 27 | 0x0034fff5, 0x01340063, 0x023400ba, 0x0334ffee, 0x8035fffb, 0x81350032, 0x823500e3, 0x8335ffef, 0x00360000, 0x0136000d, 0x023600fc, 0x0336fff8, 0x0037fff9, 0x013700fd, 0x0237000a, 0x03370000, 28 | 0x0038fff0, 0x013800e6, 0x0238002e, 0x0338fffc, 0x0039ffee, 0x013900be, 0x0239005e, 0x0339fff6, 0x003afff1, 0x013a008d, 0x023a0092, 0x033afff0, 0x003bfff6, 0x013b0059, 0x023b00c3, 0x033bffee, 29 | 0x803cfffc, 0x813c002b, 0x823c00e9, 0x833cfff0, 0x003d0000, 0x013d0008, 0x023d00fe, 0x033dfffa, 0x003efff7, 0x013e00fa, 0x023e0010, 0x033effff, 0x003fffef, 0x013f00e0, 0x023f0036, 0x033ffffb, 30 | 0x0040ffee, 0x014000b6, 0x02400067, 0x0340fff5, 0x0041fff2, 0x01410084, 0x0241009b, 0x0341fff0, 0x0042fff8, 0x01420050, 0x024200cb, 0x0342ffee, 0x8043fffd, 0x81430023, 0x824300ee, 0x8343fff1, 31 | 0x00440000, 0x01440003, 0x024400ff, 0x0344fffd, 0x0045fff5, 0x014500f7, 0x02450016, 0x0345ffff, 0x0046ffee, 0x014600d9, 0x0246003f, 0x0346fffa, 0x0047ffee, 0x014700ad, 0x02470071, 0x0347fff4, 32 | 0x0048fff3, 0x0148007a, 0x024800a4, 0x0348ffef, 0x8049fff9, 0x81490047, 0x824900d2, 0x8349ffee, 0x004afffe, 0x014a001c, 0x024a00f3, 0x034afff3, 33 | }; 34 | 35 | // Scaler coefficients generated with the following parameters: 36 | // delta_offset=0.19999999999999996 37 | // n_phases=5 38 | // n_taps=4 39 | 40 | const uint32_t scaler_coef_4upto5 [20] = { 41 | 0x00000000, 0x01000100, 0x02000000, 0x03000000, 0x0001fff0, 0x010100e9, 0x0201002b, 0x0301fffc, 0x0002ffee, 0x010200b2, 0x0202006c, 0x0302fff4, 0x8003fff4, 0x8103006c, 0x820300b2, 0x8303ffee, 42 | 0x0004fffc, 0x0104002b, 0x020400e9, 0x0304fff0, 43 | }; 44 | 45 | 46 | static void load_height(const uint32_t* coeffs, uint32_t len, uint32_t phases){ 47 | uint32_t bank_mask = 0; 48 | if(pipeline_config_scaler_bank_read()){ 49 | bank_mask |= 0x40000000; 50 | } 51 | 52 | for(int i = 0; i < len; i++){ 53 | scaler_height_coeff_data_write(coeffs[i] | bank_mask); 54 | } 55 | scaler_height_phases_write(phases); 56 | } 57 | 58 | static void load_width(const uint32_t* coeffs, uint32_t len, uint32_t phases){ 59 | uint32_t bank_mask = 0; 60 | if(pipeline_config_scaler_bank_read()){ 61 | bank_mask |= 0x40000000; 62 | } 63 | 64 | for(int i = 0; i < len; i++){ 65 | scaler_width_coeff_data_write(coeffs[i] | bank_mask); 66 | } 67 | scaler_width_phases_write(phases); 68 | } 69 | 70 | static void load_64upto75(void (*func)(const uint32_t*, uint32_t, uint32_t)){ 71 | func(scaler_coef_64upto75, sizeof(scaler_coef_64upto75) / sizeof(const uint32_t), 75); 72 | } 73 | 74 | static void load_4upto5(void (*func)(const uint32_t*, uint32_t, uint32_t)){ 75 | func(scaler_coef_4upto5, sizeof(scaler_coef_4upto5) / sizeof(const uint32_t), 5); 76 | } 77 | 78 | 79 | void init_scaler_640x512_upto_800x600(void){ 80 | load_64upto75(load_height); 81 | load_4upto5(load_width); 82 | } 83 | 84 | void init_scaler_640x512_upto_750x600(void){ 85 | load_64upto75(load_height); 86 | load_64upto75(load_width); 87 | } 88 | 89 | void init_scaler_640x512_upto_800x640(void){ 90 | load_4upto5(load_height); 91 | load_4upto5(load_width); 92 | } 93 | 94 | void switch_mode(int mode){ 95 | 96 | /* Toggle bank, will be applied on write to update_values() */ 97 | pipeline_config_scaler_bank_write(pipeline_config_scaler_bank_read() == 0 ? 1 : 0); 98 | pipeline_config_scaler_fill_write(0); 99 | 100 | if(mode == 0){ /* 1:1 */ 101 | pipeline_config_x_start_write(213 + (800-640)/2); 102 | pipeline_config_y_start_write(27 + (600-512)/2); 103 | pipeline_config_x_stop_write(640 + pipeline_config_x_start_read()); 104 | pipeline_config_y_stop_write(512 + pipeline_config_y_start_read()); 105 | 106 | pipeline_config_scaler_enable_write(0); 107 | } 108 | 109 | if(mode == 1){ /* Full Screen */ 110 | pipeline_config_x_start_write(213); 111 | pipeline_config_y_start_write(27); 112 | pipeline_config_x_stop_write(800 + pipeline_config_x_start_read()); 113 | pipeline_config_y_stop_write(600-2 + pipeline_config_y_start_read()); 114 | 115 | 116 | init_scaler_640x512_upto_800x600(); 117 | 118 | pipeline_config_scaler_enable_write(1); 119 | } 120 | 121 | if(mode == 2){ /* Fit Screen */ 122 | pipeline_config_x_start_write(213 + 25); 123 | pipeline_config_y_start_write(27); 124 | pipeline_config_x_stop_write(750 + pipeline_config_x_start_read()); 125 | pipeline_config_y_stop_write(600-2 + pipeline_config_y_start_read()); 126 | 127 | init_scaler_640x512_upto_750x600(); 128 | 129 | pipeline_config_scaler_enable_write(1); 130 | } 131 | 132 | if(mode == 3){ /* Fill Screen */ 133 | pipeline_config_x_start_write(213); 134 | pipeline_config_y_start_write(24); 135 | pipeline_config_x_stop_write(800 + pipeline_config_x_start_read()); 136 | pipeline_config_y_stop_write(618 + pipeline_config_y_start_read()); 137 | 138 | init_scaler_640x512_upto_800x640(); 139 | 140 | pipeline_config_scaler_enable_write(1); 141 | pipeline_config_scaler_fill_write(1); 142 | } 143 | 144 | /* Use new parameters */ 145 | pipeline_config_update_values_write(1); 146 | } 147 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/settings.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "settings.h" 3 | 4 | #include "include/dma.h" 5 | #include "include/scaler.h" 6 | #include "include/time.h" 7 | #include "include/boson.h" 8 | 9 | #include "crc.h" 10 | 11 | #include 12 | 13 | settings_t _settings; 14 | 15 | uint16_t _firmware_hash; 16 | 17 | 18 | const char* palette_value(const menu_item_t* p){ 19 | switch(*(uint8_t*)p->pdata){ 20 | case 0: return "White Hot"; 21 | case 1: return "Black Hot"; 22 | case 2: return "Rainbow"; 23 | case 3: return "Rainbow High Contrast"; 24 | case 4: return "Ironbow"; 25 | case 5: return "Lava"; 26 | case 6: return "Arctic"; 27 | case 7: return "Glowbow"; 28 | case 8: return "Graded Fire"; 29 | case 9: return "Hottest"; 30 | default: return "Invalid"; 31 | } 32 | } 33 | 34 | const char* scaler_value(const menu_item_t* p){ 35 | switch(*(uint8_t*)p->pdata){ 36 | case 0: return "1:1, Center"; 37 | case 1: return "Full Screen"; 38 | case 2: return "Fit"; 39 | case 3: return "Fill"; 40 | default: return "Invalid"; 41 | } 42 | } 43 | 44 | const char* enabled_disabled_value(const menu_item_t* p){ 45 | if(*(uint8_t*)p->pdata){ 46 | return "Enabled"; 47 | } 48 | return "Disabled"; 49 | } 50 | 51 | const char* boolean_value(const menu_item_t* p){ 52 | if(*(uint8_t*)p->pdata){ 53 | return "true"; 54 | } 55 | return "false"; 56 | } 57 | 58 | void basic_integer(menu_item_t* p, event_t e){ 59 | uint8_t* v = (uint8_t*)p->pdata; 60 | switch(e){ 61 | case BUTTON_A_PRESS: 62 | if(*v < p->value_max){ 63 | *v = *v + 1; 64 | } else { 65 | *v = p->value_min; /* Wrap around */ 66 | } 67 | break; 68 | 69 | case BUTTON_B_PRESS: 70 | if(*v > p->value_min){ 71 | *v = *v - 1; 72 | } else { 73 | *v = p->value_max; /* Wrap around */ 74 | } 75 | break; 76 | default: 77 | break; 78 | } 79 | } 80 | 81 | void boson_set_lut(uint32_t lut); 82 | 83 | void boson_palette_changed(const menu_item_t* p){ 84 | uint8_t lut = *(uint8_t*)p->pdata; 85 | boson_set_lut(lut); 86 | } 87 | 88 | 89 | void _boson_scaler_change(const menu_item_t* p){ 90 | uint8_t enabled = *(uint8_t*)p->pdata; 91 | switch_mode(enabled); 92 | } 93 | 94 | void boson_averager_changed(const menu_item_t* p){ 95 | uint8_t en = *(uint8_t*)p->pdata; 96 | /* Averager will stop current stream, and then startup a new stream. 97 | Because of this we should also be disabling the DMA, and then re-enabling it*/ 98 | stop_dma(); 99 | 100 | boson_set_averager(en); 101 | 102 | msleep(100); 103 | start_dma(); 104 | } 105 | 106 | void boson_frame_info_overlay(const menu_item_t* p){ 107 | 108 | } 109 | 110 | void boson_debug_info_overlay(const menu_item_t* p){ 111 | 112 | } 113 | 114 | const menu_item_t mi_palette = { 115 | .name = "Colour Palette", 116 | .value = palette_value, 117 | .pdata = &_settings.pallete, 118 | .act = basic_integer, 119 | .on_change = boson_palette_changed, 120 | .value_min = 0, 121 | .value_max = 9, 122 | }; 123 | 124 | const menu_item_t mi_scaler = { 125 | .name = "Scaler", 126 | .value = scaler_value, 127 | .pdata = &_settings.scaler_enable, 128 | .act = basic_integer, 129 | .on_change= _boson_scaler_change, 130 | .value_min = 0, 131 | .value_max = 3, 132 | }; 133 | 134 | const menu_item_t mi_averager = { 135 | .name = "Averager", 136 | .value = enabled_disabled_value, 137 | .pdata = &_settings.averager, 138 | .act = basic_integer, 139 | .on_change = boson_averager_changed, 140 | .value_min = 0, 141 | .value_max = 1, 142 | }; 143 | 144 | const menu_item_t mi_frame_info_overlay = { 145 | .name = "Frame Info", 146 | .value = enabled_disabled_value, 147 | .pdata = &_settings.frame_info_overlay, 148 | .act = basic_integer, 149 | .on_change = boson_frame_info_overlay, 150 | .value_min = 0, 151 | .value_max = 1, 152 | }; 153 | 154 | const menu_item_t mi_debug_info_overlay = { 155 | .name = "Debug Info", 156 | .value = enabled_disabled_value, 157 | .pdata = &_settings.debug_info_overlay, 158 | .act = basic_integer, 159 | .on_change = boson_debug_info_overlay, 160 | .value_min = 0, 161 | .value_max = 1, 162 | }; 163 | 164 | 165 | 166 | const menu_item_t* setting_menu_items[4] = { 167 | &mi_palette, 168 | &mi_scaler, 169 | &mi_averager, 170 | //&mi_frame_info_overlay, 171 | &mi_debug_info_overlay 172 | }; 173 | 174 | 175 | 176 | const settings_t setting_defaults = { 177 | .scaler_enable=1, 178 | }; 179 | 180 | void init_settings(bool load_defaults){ 181 | /* Calculate our Firmware CRC */ 182 | _firmware_hash = crc16(ROM_BASE, ROM_SIZE); 183 | 184 | settings_t loaded_settings; 185 | /* Read settings from EEPROM */ 186 | i2c_reset(); 187 | i2c_read(0x50, 0, &loaded_settings, sizeof(settings_t)); 188 | 189 | if(load_defaults || !validate(&loaded_settings)){ 190 | memcpy(&_settings, &setting_defaults, sizeof(settings_t)); 191 | settings_save(); 192 | }else { 193 | memcpy(&_settings, &loaded_settings, sizeof(settings_t)); 194 | } 195 | } 196 | 197 | int validate(settings_t* s){ 198 | if(s->firmware_hash != _firmware_hash){ 199 | return 0; 200 | } 201 | 202 | uint16_t crc = s->settings_crc; 203 | s->settings_crc = 0; 204 | 205 | if(crc != crc16(s, sizeof(settings_t))){ 206 | return 0; 207 | } 208 | 209 | return 1; 210 | } 211 | 212 | void settings_save(void){ 213 | /* Commit setting to EEPROM */ 214 | create_hashes(); 215 | i2c_write(0x50, 0, _settings, sizeof(settings_t)); 216 | } 217 | 218 | void create_hashes(void){ 219 | _settings.firmware_hash = _firmware_hash; 220 | _settings.settings_crc = 0; /* Crear CRC for CRC calculation */ 221 | _settings.settings_crc = crc16(&_settings, sizeof(settings_t)); 222 | } 223 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/terminal.c: -------------------------------------------------------------------------------- 1 | #include "include/terminal.h" 2 | #include 3 | 4 | static uint8_t current_colour; 5 | 6 | static uint8_t cursor_x = 0; 7 | static uint8_t cursor_y = 0; 8 | 9 | #define TERMINAL_WIDTH 100 10 | #define TERMINAL_HEIGHT 50 11 | 12 | 13 | void terminal_write_xy(uint32_t x, uint32_t y, uint8_t c, uint8_t colour){ 14 | volatile uint32_t* terminal_mem = (volatile uint32_t*) (TERMINAL_BASE); 15 | 16 | /* Check bounds */ 17 | if((x < 100) && (y < 60)){ 18 | terminal_mem += x*2 + y*100*2; 19 | *terminal_mem++ = c; 20 | *terminal_mem = colour; 21 | } 22 | } 23 | 24 | void terminal_write(uint8_t c){ 25 | 26 | if(c == '\r'){ 27 | cursor_x = 0; 28 | }else if(c == '\n'){ 29 | cursor_y += 1; 30 | if(cursor_y >= TERMINAL_HEIGHT){ 31 | cursor_y = 0; 32 | } 33 | }else{ 34 | if(cursor_x >= TERMINAL_WIDTH){ 35 | cursor_x = 0; 36 | cursor_y += 1; 37 | } 38 | 39 | terminal_write_xy(cursor_x, cursor_y, c, current_colour); 40 | cursor_x += 1; 41 | } 42 | } 43 | 44 | 45 | void terminal_set_cursor(uint8_t x, uint8_t y){ 46 | cursor_x = x; 47 | cursor_y = y; 48 | } 49 | 50 | void terminal_set_fg(colours_t fg){ 51 | current_colour &= ~0x0F; 52 | current_colour |= (fg & 0xF); 53 | } 54 | 55 | void terminal_set_bg(colours_t bg){ 56 | current_colour &= ~0xF0; 57 | current_colour |= ((bg & 0xF) << 4); 58 | } 59 | 60 | void terminal_clear(void){ 61 | volatile uint32_t* vga = (volatile uint32_t*) (TERMINAL_BASE); 62 | for(int i = 0; i < 100*50; i++){ 63 | vga[i*2+1] = current_colour; 64 | vga[i*2] = 0; 65 | } 66 | 67 | cursor_x = 0; 68 | cursor_y = 0; 69 | } 70 | 71 | void terminal_fill(uint8_t x, uint8_t y, uint8_t w, uint8_t h){ 72 | 73 | /* Fill */ 74 | for(int i = y; i <= (y+h); i++){ 75 | for(int j = x; j <= (x+w); j++){ 76 | terminal_write_xy(j, i, ' ', current_colour); 77 | } 78 | } 79 | } 80 | 81 | void treminal_draw_box(uint8_t x, uint8_t y, uint8_t w, uint8_t h){ 82 | uint8_t box_colour = TERMINAL_BLUE << 4 | TERMINAL_WHITE; 83 | for(int i = x+1; i < (x+w); i++){ 84 | /* ─ : 0xc4 in CP437 */ 85 | terminal_write_xy(i, y, 0xc4,box_colour); 86 | terminal_write_xy(i, y + h, 0xc4,box_colour); 87 | } 88 | 89 | for(int i = y+1; i < (y+h); i++){ 90 | /* │ : 0xb3 in CP437 */ 91 | terminal_write_xy(x, i, 0xb3,box_colour); 92 | terminal_write_xy(x + w, i, 0xb3,box_colour); 93 | } 94 | 95 | /* Corners */ 96 | terminal_write_xy(x, y, /* ┌ */ 0xda, box_colour); 97 | terminal_write_xy(x, y+h, /* └ */ 0xc0, box_colour); 98 | terminal_write_xy(x+w, y, /* ┐ */ 0xbf, box_colour); 99 | terminal_write_xy(x+w, y+h, /* ┘ */ 0xd9, box_colour); 100 | 101 | /* Fill */ 102 | for(int i = y+1; i < (y+h); i++){ 103 | for(int j = x+1; j < (x+w); j++){ 104 | terminal_write_xy(j, i, ' ',box_colour); 105 | } 106 | } 107 | 108 | /* Drop shadow */ 109 | for(int i = x+1; i <= (x+w+1); i++){ 110 | terminal_write_xy(i, y + h + 1, ' ', 0); 111 | } 112 | 113 | for(int i = y+1; i <= (y+h+1); i++){ 114 | terminal_write_xy(x + w + 1 , i, ' ', 0); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /firmware/DiVA-fw/terminal_menu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "terminal.h" 3 | #include "terminal_menu.h" 4 | #include "settings.h" 5 | 6 | 7 | typedef void (*on_exit_t)(void); 8 | typedef void (*on_entry_t)(void); 9 | typedef void (*act_t)(event_t); 10 | 11 | typedef struct { 12 | on_entry_t on_entry; 13 | on_exit_t on_exit; 14 | act_t act; 15 | } menu_t; 16 | 17 | 18 | void settings_act(event_t e); 19 | void settings_on_entry(); 20 | void settings_on_exit(); 21 | 22 | void settings_change_act(event_t e); 23 | void settings_change_on_entry(); 24 | void settings_change_on_exit(); 25 | 26 | void null_act(event_t e); 27 | void null_on_entry(); 28 | void null_on_exit(); 29 | 30 | const menu_t menu_settings = { 31 | .on_entry = settings_on_entry, 32 | .on_exit = settings_on_exit, 33 | .act = settings_act 34 | }; 35 | 36 | const menu_t menu_settings_change = { 37 | .on_entry = settings_change_on_entry, 38 | .on_exit = settings_change_on_exit, 39 | .act = settings_change_act 40 | }; 41 | 42 | const menu_t null_menu = { 43 | .on_entry = null_on_entry, 44 | .on_exit = null_on_exit, 45 | .act = null_act 46 | }; 47 | 48 | menu_t* current_menu = &null_menu; 49 | 50 | void menu_act(event_t e){ 51 | menu_t* _old = current_menu; 52 | current_menu->act(e); 53 | 54 | /* Handle switching states */ 55 | if(current_menu != _old){ 56 | _old->on_exit(); 57 | current_menu->on_entry(); 58 | } 59 | } 60 | 61 | void highlight_colours(){ 62 | terminal_set_fg(TERMINAL_BLUE); 63 | terminal_set_bg(TERMINAL_WHITE); 64 | } 65 | 66 | void standard_colours(){ 67 | terminal_set_fg(TERMINAL_WHITE); 68 | terminal_set_bg(TERMINAL_BLUE); 69 | } 70 | 71 | 72 | 73 | 74 | 75 | static int selected_index = 0; 76 | const int index_total = sizeof(setting_menu_items)/sizeof(menu_item_t*); 77 | 78 | void draw_menu_item(int idx){ 79 | terminal_set_cursor(19, 21+idx); 80 | printf("%s [ %s ]", setting_menu_items[idx]->name, setting_menu_items[idx]->value(setting_menu_items[idx])); 81 | } 82 | 83 | void draw_menu_item_highlight(int idx){ 84 | terminal_set_cursor(19, 21+idx); 85 | standard_colours(); 86 | printf("%s [", setting_menu_items[idx]->name); 87 | highlight_colours(); 88 | printf(" %s ", setting_menu_items[idx]->value(setting_menu_items[idx])); 89 | standard_colours(); 90 | printf("]"); 91 | } 92 | 93 | void fill_row(uint32_t idx){ 94 | terminal_set_cursor(17, 21+idx); 95 | 96 | for(int i = 0; i < 67; i++){ 97 | terminal_write(' '); 98 | } 99 | } 100 | 101 | void settings_act(event_t e){ 102 | 103 | const uint32_t width = 70; 104 | const uint32_t height = 10; 105 | const uint32_t window_x = (100 - width) / 2; 106 | const uint32_t window_y = 20; 107 | 108 | switch(e){ 109 | case BUTTON_A_HOLD: 110 | current_menu = &menu_settings_change; 111 | break; 112 | case BUTTON_A_PRESS: 113 | 114 | if(selected_index > 0){ 115 | standard_colours(); 116 | fill_row(selected_index); 117 | draw_menu_item(selected_index); 118 | 119 | selected_index--; 120 | highlight_colours(); 121 | fill_row(selected_index); 122 | draw_menu_item(selected_index); 123 | } 124 | break; 125 | case BUTTON_B_HOLD: 126 | current_menu = &null_menu; 127 | break; 128 | case BUTTON_B_PRESS: 129 | 130 | if(selected_index < (index_total-1)){ 131 | standard_colours(); 132 | fill_row(selected_index); 133 | draw_menu_item(selected_index); 134 | 135 | selected_index++; 136 | highlight_colours(); 137 | fill_row(selected_index); 138 | draw_menu_item(selected_index); 139 | } 140 | break; 141 | } 142 | } 143 | 144 | void settings_on_entry(){ 145 | /* Draw the main settings window */ 146 | const uint32_t width = 70; 147 | const uint32_t height = 10; 148 | const uint32_t window_x = (100 - width) / 2; 149 | const uint32_t window_y = 20; 150 | 151 | 152 | terminal_set_bg(TERMINAL_BLUE); 153 | terminal_set_fg(TERMINAL_WHITE); 154 | 155 | treminal_draw_box(window_x, window_y, width, height); 156 | terminal_set_cursor(window_x + 1, window_y); 157 | printf("Settings"); 158 | 159 | 160 | terminal_set_fg(TERMINAL_BLUE); 161 | terminal_set_bg(TERMINAL_WHITE); 162 | if((selected_index < 0) || (selected_index > (index_total-1))){ 163 | selected_index = 0; 164 | } 165 | 166 | //printf("LUT [%s]%.*s", palette[0], width - 10 - strlen(palette[0]), padding); 167 | for(int idx = 0; idx < index_total; idx++){ 168 | standard_colours(); 169 | if(idx == selected_index){ 170 | highlight_colours(); 171 | fill_row(idx); 172 | } 173 | draw_menu_item(idx); 174 | } 175 | 176 | } 177 | 178 | void settings_on_exit(){ 179 | } 180 | 181 | 182 | 183 | 184 | void settings_change_act(event_t e){ 185 | 186 | 187 | 188 | switch(e){ 189 | case BUTTON_B_HOLD: 190 | current_menu = &menu_settings; 191 | break; 192 | default: 193 | break; 194 | } 195 | 196 | setting_menu_items[selected_index]->act(setting_menu_items[selected_index], e); 197 | 198 | standard_colours(); 199 | fill_row(selected_index); 200 | draw_menu_item_highlight(selected_index); 201 | 202 | if(setting_menu_items[selected_index]->on_change){ 203 | setting_menu_items[selected_index]->on_change(setting_menu_items[selected_index]); 204 | } 205 | } 206 | 207 | void settings_change_on_entry(){ 208 | standard_colours(); 209 | fill_row(selected_index); 210 | draw_menu_item_highlight(selected_index); 211 | } 212 | 213 | void settings_change_on_exit(){ 214 | settings_save(); 215 | } 216 | 217 | 218 | 219 | 220 | 221 | void null_act(event_t e){ 222 | switch(e){ 223 | case BUTTON_A_HOLD: 224 | current_menu = &menu_settings; 225 | break; 226 | case BUTTON_A_PRESS: 227 | break; 228 | case BUTTON_B_HOLD: 229 | break; 230 | case BUTTON_B_PRESS: 231 | break; 232 | } 233 | } 234 | 235 | void null_on_entry(){ 236 | /* Clear screen */ 237 | const uint32_t width = 70; 238 | const uint32_t height = 10; 239 | const uint32_t window_x = (100 - width) / 2; 240 | const uint32_t window_y = 20; 241 | 242 | terminal_set_fg(TERMINAL_TRANSPARENT); 243 | terminal_set_bg(TERMINAL_TRANSPARENT); 244 | terminal_fill(window_x, window_y, width+1, height+1); 245 | } 246 | 247 | void null_on_exit(){ 248 | } -------------------------------------------------------------------------------- /firmware/DiVA-fw/time.c: -------------------------------------------------------------------------------- 1 | #include "include/time.h" 2 | #include 3 | 4 | void msleep(int ms) 5 | { 6 | timer0_en_write(0); 7 | timer0_reload_write(0); 8 | timer0_load_write(CONFIG_CLOCK_FREQUENCY/1000*ms); 9 | timer0_en_write(1); 10 | timer0_update_value_write(1); 11 | while(timer0_value_read()) timer0_update_value_write(1); 12 | } -------------------------------------------------------------------------------- /gateware/rtl/buffered_csr_block.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright (c) 2020 Gregory Davill 2 | # License: BSD 3 | 4 | from migen import * 5 | from litex.soc.interconnect.stream import Endpoint 6 | from rtl.edge_detect import EdgeDetect 7 | 8 | from litex.soc.interconnect.csr import AutoCSR, CSR, CSRStorage 9 | from litex.soc.interconnect.stream import AsyncFIFO 10 | 11 | class BufferedCSRBlock(Module, AutoCSR): 12 | def __init__(self, params): 13 | 14 | # Sync signal, 15 | self.csr_sync = csr_sync = Signal() 16 | 17 | # Populate CSR storage 18 | for name,size in params: 19 | csr = CSRStorage(name=name, size=size) 20 | setattr(self, f'{name}_csr', csr) 21 | 22 | # output signal 23 | sig = Signal(size, name=name) 24 | setattr(self, f'{name}', sig) 25 | 26 | # CSR control 27 | self.update_values = CSR(1) 28 | 29 | # fifo 30 | self.submodules.fifo = fifo = ClockDomainsRenamer({"read":"video","write":"sys"})(AsyncFIFO(params, depth=4)) 31 | 32 | for name,size in params: 33 | self.comb += getattr(fifo.sink, name).eq(getattr(self, f'{name}_csr').storage) 34 | self.sync.video += [ 35 | fifo.source.ready.eq(csr_sync), 36 | If(csr_sync, 37 | If(fifo.source.valid, 38 | getattr(self, f'{name}').eq(getattr(fifo.source, name)), 39 | ) 40 | ) 41 | ] 42 | 43 | self.comb += [ 44 | fifo.sink.valid.eq(self.update_values.re), 45 | ] 46 | -------------------------------------------------------------------------------- /gateware/rtl/buttons.py: -------------------------------------------------------------------------------- 1 | from migen import * 2 | from migen.genlib.cdc import MultiReg 3 | from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRField 4 | 5 | class Button(Module, AutoCSR): 6 | def __init__(self, pads): 7 | 8 | self.events = CSRStatus(32) 9 | self.raw = CSRStatus(32) 10 | 11 | events = Signal(32) 12 | raw = Signal(32) 13 | 14 | PRESS_TIME = int(82.5e6 * 5e-3) 15 | HOLD_TIME = int(82.5e6 * 750e-3) 16 | 17 | button_a_counter = Signal(max=max(PRESS_TIME, HOLD_TIME)) 18 | button_b_counter = Signal(max=max(PRESS_TIME, HOLD_TIME)) 19 | 20 | button_a_sig = Signal() 21 | button_b_sig = Signal() 22 | 23 | # de glitch 24 | self.specials += MultiReg(~pads.a, button_a_sig, n=4) 25 | self.specials += MultiReg(~pads.b, button_b_sig, n=4) 26 | 27 | self.sync += [ 28 | If(self.events.we, 29 | events.eq(0) 30 | ), 31 | 32 | If(button_a_sig, 33 | If(button_a_counter < HOLD_TIME, 34 | button_a_counter.eq(button_a_counter + 1) 35 | ), 36 | If(button_a_counter == (HOLD_TIME - 1), 37 | events[2].eq(1) 38 | ) 39 | ).Else( 40 | If((button_a_counter > PRESS_TIME) & (button_a_counter < HOLD_TIME), 41 | events[0].eq(1) 42 | ), 43 | button_a_counter.eq(0), 44 | ), 45 | 46 | If(button_b_sig, 47 | If(button_b_counter < HOLD_TIME, 48 | button_b_counter.eq(button_b_counter + 1) 49 | ), 50 | If(button_b_counter == (HOLD_TIME - 1), 51 | events[3].eq(1) 52 | ) 53 | ).Else( 54 | If((button_b_counter > PRESS_TIME) & (button_b_counter < HOLD_TIME), 55 | events[1].eq(1) 56 | ), 57 | button_b_counter.eq(0), 58 | ) 59 | ] 60 | 61 | self.sync += [ 62 | raw.eq(0), 63 | If(button_a_counter > PRESS_TIME, 64 | raw[0].eq(1) 65 | ), 66 | If(button_b_counter > PRESS_TIME, 67 | raw[1].eq(1) 68 | ), 69 | ] 70 | 71 | self.comb += [ 72 | self.events.status.eq(events), 73 | self.raw.status.eq(raw) 74 | ] 75 | 76 | -------------------------------------------------------------------------------- /gateware/rtl/ecp5_dynamic_pll.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright (c) 2018-2020 Florent Kermarrec 2 | # This file is Copyright (c) 2019 Michael Betz 3 | # License: BSD 4 | 5 | """Clock Abstraction Modules""" 6 | 7 | import math 8 | import logging 9 | 10 | from migen import * 11 | from migen.genlib.resetsync import AsyncResetSynchronizer 12 | 13 | from litex.build.io import DifferentialInput 14 | 15 | from litex.soc.integration.soc import colorer 16 | from litex.soc.interconnect.csr import * 17 | 18 | logging.basicConfig(level=logging.INFO) 19 | 20 | def period_ns(freq): 21 | return 1e9/freq 22 | 23 | # Logging ------------------------------------------------------------------------------------------ 24 | 25 | def register_clkin_log(logger, clkin, freq): 26 | logger.info("Registering {} {} of {}.".format( 27 | colorer("Differential") if isinstance(clkin, Record) else colorer("Single Ended"), 28 | colorer("ClkIn"), 29 | colorer("{:3.2f}MHz".format(freq/1e6)) 30 | )) 31 | 32 | def create_clkout_log(logger, name, freq, margin, nclkouts): 33 | logger.info("Creating {} of {} {}.".format( 34 | colorer("ClkOut{} {}".format(nclkouts, name)), 35 | colorer("{:3.2f}MHz".format(freq/1e6)), 36 | "(+-{:3.2f}ppm)".format(margin*1e6), 37 | )) 38 | 39 | def compute_config_log(logger, config): 40 | log = "Config:\n" 41 | length = 0 42 | for name in config.keys(): 43 | if len(name) > length: length = len(name) 44 | for name, value in config.items(): 45 | if "freq" in name or "vco" in name: 46 | value = "{:3.2f}MHz".format(value/1e6) 47 | if "phase" in name: 48 | value = "{:3.2f}°".format(value) 49 | log += "{}{}: {}\n".format(name, " "*(length-len(name)), value) 50 | log = log[:-1] 51 | logger.info(log) 52 | 53 | # Helpers ------------------------------------------------------------------------------------------ 54 | 55 | def clkdiv_range(start, stop, step=1): 56 | start = float(start) 57 | stop = float(stop) 58 | step = float(step) 59 | current = start 60 | while current < stop: 61 | yield int(current) if math.floor(current) == current else current 62 | current += step 63 | 64 | class ECP5PLL(Module): 65 | nclkouts_max = 3 66 | clki_div_range = (1, 128+1) 67 | clkfb_div_range = (1, 128+1) 68 | clko_div_range = (1, 128+1) 69 | clki_freq_range = ( 8e6, 400e6) 70 | clko_freq_range = (3.125e6, 400e6) 71 | vco_freq_range = ( 400e6, 800e6) 72 | 73 | def __init__(self): 74 | self.logger = logging.getLogger("ECP5PLL") 75 | self.logger.info("Creating ECP5PLL.") 76 | self.reset = Signal() 77 | self.locked = Signal() 78 | self.clkin_freq = None 79 | self.vcxo_freq = None 80 | self.nclkouts = 0 81 | self.clkouts = {} 82 | self.config = {} 83 | self.params = {} 84 | 85 | self.phase_sel = Signal(2) 86 | self.phase_dir = Signal() 87 | self.phase_step = Signal() 88 | self.phase_load = Signal() 89 | 90 | def register_clkin(self, clkin, freq): 91 | (clki_freq_min, clki_freq_max) = self.clki_freq_range 92 | assert freq >= clki_freq_min 93 | assert freq <= clki_freq_max 94 | self.clkin = Signal() 95 | if isinstance(clkin, (Signal, ClockSignal)): 96 | self.comb += self.clkin.eq(clkin) 97 | else: 98 | raise ValueError 99 | self.clkin_freq = freq 100 | register_clkin_log(self.logger, clkin, freq) 101 | 102 | def create_clkout(self, cd, freq, phase=0, margin=1e-2): 103 | (clko_freq_min, clko_freq_max) = self.clko_freq_range 104 | assert freq >= clko_freq_min 105 | # assert freq <= clko_freq_max 106 | assert self.nclkouts < self.nclkouts_max 107 | clkout = Signal() 108 | self.clkouts[self.nclkouts] = (clkout, freq, phase, margin) 109 | self.comb += cd.clk.eq(clkout) 110 | create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts) 111 | self.nclkouts += 1 112 | 113 | def compute_config(self): 114 | config = {} 115 | for clki_div in range(*self.clki_div_range): 116 | config["clki_div"] = clki_div 117 | for clkfb_div in range(*self.clkfb_div_range): 118 | all_valid = True 119 | vco_freq = self.clkin_freq/clki_div*clkfb_div*1 # clkos3_div=1 120 | (vco_freq_min, vco_freq_max) = self.vco_freq_range 121 | if vco_freq >= vco_freq_min and vco_freq <= vco_freq_max: 122 | for n, (clk, f, p, m) in sorted(self.clkouts.items()): 123 | valid = False 124 | for d in range(*self.clko_div_range): 125 | clk_freq = vco_freq/d 126 | if abs(clk_freq - f) <= f*m: 127 | config["clko{}_freq".format(n)] = clk_freq 128 | config["clko{}_div".format(n)] = d 129 | config["clko{}_phase".format(n)] = p 130 | valid = True 131 | break 132 | if not valid: 133 | all_valid = False 134 | else: 135 | all_valid = False 136 | if all_valid: 137 | config["vco"] = vco_freq 138 | config["clkfb_div"] = clkfb_div 139 | compute_config_log(self.logger, config) 140 | return config 141 | raise ValueError("No PLL config found") 142 | 143 | def do_finalize(self): 144 | config = self.compute_config() 145 | clkfb = Signal() 146 | self.params.update( 147 | attr=[ 148 | ("ICP_CURRENT", "6"), 149 | ("LPF_RESISTOR", "16"), 150 | ("MFG_ENABLE_FILTEROPAMP", "1"), 151 | ("MFG_GMCREF_SEL", "2")], 152 | i_RST = self.reset, 153 | i_CLKI = self.clkin, 154 | o_LOCK = self.locked, 155 | p_FEEDBK_PATH = "INT_OS3", # CLKOS3 reserved for feedback with div=1. 156 | p_CLKOS3_ENABLE = "ENABLED", 157 | p_CLKOS3_DIV = 1, 158 | p_CLKFB_DIV = config["clkfb_div"], 159 | p_CLKI_DIV = config["clki_div"], 160 | 161 | p_DPHASE_SOURCE = "ENABLED", 162 | 163 | i_PHASESEL0= self.phase_sel[0], 164 | i_PHASESEL1= self.phase_sel[1], 165 | i_PHASEDIR= self.phase_dir, 166 | i_PHASESTEP= self.phase_step, 167 | i_PHASELOADREG=self.phase_load, 168 | ) 169 | for n, (clk, f, p, m) in sorted(self.clkouts.items()): 170 | n_to_l = {0: "P", 1: "S", 2: "S2"} 171 | div = config["clko{}_div".format(n)] 172 | cphase = int(p*(div + 1)/360 + div) 173 | self.params["p_CLKO{}_ENABLE".format(n_to_l[n])] = "ENABLED" 174 | self.params["p_CLKO{}_DIV".format(n_to_l[n])] = div 175 | self.params["p_CLKO{}_FPHASE".format(n_to_l[n])] = p 176 | self.params["p_CLKO{}_CPHASE".format(n_to_l[n])] = p 177 | self.params["o_CLKO{}".format(n_to_l[n])] = clk 178 | self.specials += Instance("EHXPLLL", **self.params) 179 | -------------------------------------------------------------------------------- /gateware/rtl/edge_detect.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright (c) 2020 Gregory Davill 2 | # License: BSD 3 | from migen import * 4 | 5 | from migen.genlib.cdc import PulseSynchronizer 6 | 7 | class EdgeDetect(Module): 8 | """A module to detect edges on a signal line 9 | 10 | Args 11 | ---- 12 | 13 | mode (str, optional): The type of edge that will generate an output pulse. 14 | The options are: 15 | * 'rise' 16 | * 'fall' 17 | * 'change' 18 | On edge a 1 cycle pulse is generated, which can optionally 19 | be sent to a different clock domain. 20 | 21 | input_cd (str, optional): Clock domain of input signal 22 | 23 | output_cd (str, optional): Clock domain of output pulse 24 | """ 25 | 26 | def __init__(self, mode="rise", input_cd="sys", output_cd="sys"): 27 | self.i = i = Signal() 28 | self.o = o = Signal() 29 | 30 | pulse = Signal() 31 | 32 | reg = Signal() 33 | v = getattr(self.sync, input_cd) 34 | v += reg.eq(i) 35 | if mode == "rise": 36 | v += pulse.eq(~reg & i) 37 | elif mode == "fall": 38 | v += pulse.eq(reg & ~i) 39 | elif mode == "change": 40 | v += pulse.eq(reg != i) 41 | else: 42 | AttributeError(f"Invalid mode={mode}") 43 | 44 | if input_cd != output_cd: 45 | ps = PulseSynchronizer(input_cd, output_cd) 46 | self.submodules += ps 47 | self.comb += [ 48 | ps.i.eq(pulse), 49 | o.eq(ps.o) 50 | ] 51 | else: 52 | self.comb += o.eq(pulse) 53 | -------------------------------------------------------------------------------- /gateware/rtl/hdmi.py: -------------------------------------------------------------------------------- 1 | from migen import * 2 | 3 | class HDMI(Module): 4 | def __init__(self, platform, pins): 5 | 6 | platform.add_source('rtl/verilog/fake_differential.v') 7 | platform.add_source('rtl/verilog/vga2dvid.v') 8 | platform.add_source('rtl/verilog/tmds_encoder.v') 9 | 10 | self.r = vga_r = Signal(8) 11 | self.g = vga_g = Signal(8) 12 | self.b = vga_b = Signal(8) 13 | self.hsync = vga_hsync = Signal() 14 | self.vsync = vga_vsync = Signal() 15 | self.blank = vga_blank = Signal() 16 | 17 | tmds = [Signal(2) for i in range(4)] 18 | self.specials += Instance( 19 | 'vga2dvid', 20 | p_C_ddr=1, 21 | i_clk_pixel=ClockSignal('video'), 22 | i_clk_shift=ClockSignal('video_shift'), 23 | i_in_red=vga_r, 24 | i_in_green=vga_g, 25 | i_in_blue=vga_b, 26 | i_in_hsync=vga_hsync, 27 | i_in_vsync=vga_vsync, 28 | i_in_blank=vga_blank, 29 | o_out_clock=tmds[3], 30 | o_out_red=tmds[2], 31 | o_out_green=tmds[1], 32 | o_out_blue=tmds[0] 33 | ) 34 | 35 | 36 | self.specials += Instance( 37 | 'fake_differential', 38 | p_C_ddr=1, 39 | i_clk_shift=ClockSignal('video_shift'), 40 | i_in_clock=tmds[3], 41 | i_in_red=tmds[2], 42 | i_in_green=tmds[1], 43 | i_in_blue=tmds[0], 44 | o_out_p=pins.p, 45 | #o_out_n=pins.n, 46 | 47 | i_move=0, 48 | i_loadn=0, 49 | i_dir=1, 50 | ) -------------------------------------------------------------------------------- /gateware/rtl/platform/bosonHDMI_r0d2.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright (c) 2020 Gregory Davill 2 | # License: BSD 3 | 4 | from litex.build.generic_platform import * 5 | from litex.build.lattice import LatticePlatform 6 | 7 | # IOs ---------------------------------------------------------------------------------------------- 8 | 9 | _io = [ 10 | ("clk48", 0, Pins("M1"), IOStandard("LVCMOS18")), 11 | ("rst_n", 0, Pins("V17"), IOStandard("LVCMOS33")), 12 | 13 | 14 | ("rgb_led", 0, 15 | Subsignal("r", Pins("L16"), IOStandard("LVCMOS33")), 16 | Subsignal("g", Pins("J16"), IOStandard("LVCMOS33")), 17 | Subsignal("b", Pins("J17"), IOStandard("LVCMOS33")), 18 | ), 19 | 20 | ("usb", 0, 21 | Subsignal("d_p", Pins("G15")), 22 | Subsignal("d_n", Pins("K15")), 23 | Subsignal("pullup", Pins("G16")), 24 | IOStandard("LVCMOS33") 25 | ), 26 | 27 | ("hdmi_i2c", 0, 28 | Subsignal("sda", Pins("M16")), 29 | Subsignal("scl", Pins("M17")), 30 | ), 31 | ("hdmi", 0, 32 | Subsignal("p", Pins("J18 G18 K17 F17"), IOStandard("LVCMOS33D"), Misc("SLEWRATE=FAST"), Misc("DRIVE=8")), 33 | #Subsignal("n", Pins("K18 H17 L18 F16"), IOStandard("LVCMOS33"), Misc("SLEWRATE=SLOW")), 34 | ), 35 | 36 | 37 | ("i2c", 0, 38 | Subsignal("sda", Pins("N15")), 39 | Subsignal("scl", Pins("N16")), 40 | ), 41 | 42 | ("hyperRAM", 0, 43 | Subsignal("rst_n", Pins("F4"), IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 44 | Subsignal("clk_p", Pins("G4"), IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 45 | Subsignal("clk_n", Pins("H4"), IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 46 | Subsignal("cs_n", Pins("F3"), IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 47 | Subsignal("dq", Pins("K4 L4 J3 K3 L1 M3 N4 N3"), IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 48 | Subsignal("rwds", Pins("H3"), IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 49 | ), 50 | 51 | ("boson", 0, 52 | Subsignal("data", Pins("D15 C17 C13 B17 A2 A10 A11 C2 \ 53 | D1 C15 B11 A15 C3 A8 C1 B13 \ 54 | B12 B1 D13 B10 B15 A16 A4 A12"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 55 | Subsignal("clk", Pins("A17"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 56 | Subsignal("vsync", Pins("A13"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 57 | Subsignal("hsync", Pins("D16"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 58 | Subsignal("valid", Pins("C16"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 59 | Subsignal("tx", Pins("A3"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 60 | Subsignal("rx", Pins("B9"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 61 | Subsignal("reset", Pins("B2"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 62 | Subsignal("ext_sync", Pins("B18"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 63 | ), 64 | 65 | ("button", 0, 66 | Subsignal("a", Pins("F2"),IOStandard("LVCMOS33"),Misc("PULLMODE=UP")), 67 | Subsignal("b", Pins("J1"),IOStandard("LVCMOS33"),Misc("PULLMODE=UP")), 68 | ), 69 | 70 | ("spiflash", 0, 71 | Subsignal("cs_n", Pins("U17"), IOStandard("LVCMOS33")), 72 | #Subsignal("clk", Pins("U16"), IOStandard("LVCMOS33")), # Note: CLK is bound using USRMCLK block 73 | Subsignal("miso", Pins("T18"), IOStandard("LVCMOS33")), 74 | Subsignal("mosi", Pins("U18"), IOStandard("LVCMOS33")), 75 | Subsignal("wp", Pins("R18"), IOStandard("LVCMOS33")), 76 | Subsignal("hold", Pins("N18"), IOStandard("LVCMOS33")), 77 | ), 78 | ("spiflash4x", 0, 79 | Subsignal("cs_n", Pins("U17"), IOStandard("LVCMOS33")), 80 | #Subsignal("clk", Pins("U16"), IOStandard("LVCMOS33")), 81 | Subsignal("dq", Pins("U18 T18 R18 N18"), IOStandard("LVCMOS33")), 82 | ), 83 | 84 | ] 85 | 86 | 87 | 88 | # Connectors --------------------------------------------------------------------------------------- 89 | 90 | _connectors = [ 91 | ] 92 | 93 | # Platform ----------------------------------------------------------------------------------------- 94 | 95 | class Platform(LatticePlatform): 96 | default_clk_name = "clk48" 97 | default_clk_period = 1e9/48e6 98 | 99 | def __init__(self, **kwargs): 100 | LatticePlatform.__init__(self, "LFE5U-25F-8MG285C", _io, _connectors, toolchain='trellis', **kwargs) 101 | 102 | -------------------------------------------------------------------------------- /gateware/rtl/platform/bosonHDMI_r0d3.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright (c) 2020 Gregory Davill 2 | # License: BSD 3 | 4 | from litex.build.generic_platform import * 5 | from litex.build.lattice import LatticePlatform 6 | 7 | # IOs ---------------------------------------------------------------------------------------------- 8 | 9 | _io = [ 10 | ("clk48", 0, Pins("M1"), IOStandard("LVCMOS18")), 11 | ("rst_n", 0, Pins("V17"), IOStandard("LVCMOS33")), 12 | 13 | ("rgb_led", 0, 14 | Subsignal("r", Pins("L16"), IOStandard("LVCMOS33")), 15 | Subsignal("g", Pins("J16"), IOStandard("LVCMOS33")), 16 | Subsignal("b", Pins("J17"), IOStandard("LVCMOS33")), 17 | ), 18 | 19 | ("usb", 0, 20 | Subsignal("d_p", Pins("G16")), 21 | Subsignal("d_n", Pins("K15")), 22 | Subsignal("pullup", Pins("H16")), 23 | Subsignal("sw_sel", Pins("F15")), 24 | Subsignal("sw_oe", Pins("G15")), 25 | IOStandard("LVCMOS33") 26 | ), 27 | 28 | ("hdmi_i2c", 0, 29 | Subsignal("sda", Pins("M16")), 30 | Subsignal("scl", Pins("M17")), 31 | ), 32 | ("hdmi_cec", 0, 33 | Subsignal("cec", Pins("L15")), 34 | ), 35 | ("hdmi", 0, 36 | Subsignal("p", Pins("F17 G18 J18 K17"), IOStandard("LVCMOS33D"), Misc("SLEWRATE=FAST")), 37 | ), 38 | 39 | ("i2c", 0, 40 | Subsignal("sda", Pins("N15")), 41 | Subsignal("scl", Pins("N16")), 42 | ), 43 | 44 | ("hyperRAM", 0, 45 | Subsignal("rst_n", Pins("F4"), IOStandard("LVCMOS18"),Misc("SLEWRATE=FAST")), 46 | Subsignal("clk_p", Pins("G4"), IOStandard("LVCMOS18"),Misc("SLEWRATE=FAST")), 47 | Subsignal("clk_n", Pins("H4"), IOStandard("LVCMOS18"),Misc("SLEWRATE=FAST")), 48 | Subsignal("cs_n", Pins("F3"), IOStandard("LVCMOS18"),Misc("SLEWRATE=FAST")), 49 | Subsignal("dq", Pins("K4 L4 J3 K3 L1 M3 N4 N3"), IOStandard("LVCMOS18"),Misc("SLEWRATE=FAST")), 50 | Subsignal("rwds", Pins("H3"), IOStandard("LVCMOS18"),Misc("SLEWRATE=FAST")), 51 | ), 52 | 53 | ("boson", 0, 54 | Subsignal("data", Pins("D15 C17 C13 B17 A2 A10 A11 C2 \ 55 | D1 A15 B11 C15 C3 A8 C1 B13 \ 56 | B12 B1 D13 B10 B15 A16 A4 A12"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 57 | Subsignal("clk", Pins("A17"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 58 | Subsignal("vsync", Pins("A13"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 59 | Subsignal("hsync", Pins("D16"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 60 | Subsignal("valid", Pins("C16"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 61 | Subsignal("tx", Pins("A3"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 62 | Subsignal("rx", Pins("B9"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 63 | Subsignal("reset", Pins("B2"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 64 | Subsignal("ext_sync", Pins("B18"),IOStandard("LVCMOS18"),Misc("SLEWRATE=SLOW")), 65 | ), 66 | 67 | ("button", 0, 68 | Subsignal("a", Pins("F2"),IOStandard("LVCMOS18"),Misc("PULLMODE=UP")), 69 | Subsignal("b", Pins("J1"),IOStandard("LVCMOS18"),Misc("PULLMODE=UP")), 70 | ), 71 | 72 | ("spiflash", 0, 73 | Subsignal("cs_n", Pins("U17"), IOStandard("LVCMOS33")), 74 | #Subsignal("clk", Pins("U16"), IOStandard("LVCMOS33")), # Note: CLK is bound using USRMCLK block 75 | Subsignal("miso", Pins("T18"), IOStandard("LVCMOS33")), 76 | Subsignal("mosi", Pins("U18"), IOStandard("LVCMOS33")), 77 | Subsignal("wp", Pins("R18"), IOStandard("LVCMOS33")), 78 | Subsignal("hold", Pins("N18"), IOStandard("LVCMOS33")), 79 | ), 80 | ("spiflash4x", 0, 81 | Subsignal("cs_n", Pins("U17"), IOStandard("LVCMOS33")), 82 | #Subsignal("clk", Pins("U16"), IOStandard("LVCMOS33")), 83 | Subsignal("dq", Pins("U18 T18 R18 N18"), IOStandard("LVCMOS33")), 84 | ), 85 | ] 86 | 87 | 88 | 89 | # Connectors --------------------------------------------------------------------------------------- 90 | 91 | _connectors = [ 92 | ] 93 | 94 | # Platform ----------------------------------------------------------------------------------------- 95 | 96 | class Platform(LatticePlatform): 97 | default_clk_name = "clk48" 98 | default_clk_period = 1e9/48e6 99 | 100 | def __init__(self, **kwargs): 101 | LatticePlatform.__init__(self, "LFE5U-25F-8MG285C", _io, _connectors, toolchain='trellis', **kwargs) 102 | 103 | -------------------------------------------------------------------------------- /gateware/rtl/prbs.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright (c) 2020 Gregory Davill 2 | # License: BSD 3 | 4 | from migen import * 5 | from litex.soc.interconnect.stream import EndpointDescription, Endpoint 6 | from litex.soc.interconnect.csr import CSR, CSRStatus, AutoCSR 7 | from litex.soc.cores.prbs import PRBS31Generator 8 | 9 | class PRBSSource(Module, AutoCSR): 10 | def __init__(self): 11 | self.source = source = Endpoint([("data", 32)]) 12 | 13 | self.submodules.prbs = prbs = ResetInserter()(CEInserter()(PRBS31Generator(32))) 14 | 15 | self.reset = CSR(1) 16 | 17 | self.comb += [ 18 | source.valid.eq(1), 19 | source.data.eq(prbs.o), 20 | 21 | prbs.ce.eq(source.ready), 22 | prbs.reset.eq(self.reset.re) 23 | ] 24 | 25 | 26 | class PRBSSink(Module, AutoCSR): 27 | def __init__(self): 28 | self.sink = sink = Endpoint([("data", 32)]) 29 | 30 | self.submodules.prbs = prbs = ResetInserter()(CEInserter()(PRBS31Generator(32))) 31 | 32 | self.reset = CSR(1) 33 | self.good = CSRStatus(32) 34 | self.bad = CSRStatus(32) 35 | 36 | good = Signal(32) 37 | bad = Signal(32) 38 | 39 | self.sync += [ 40 | If(self.reset.re, 41 | good.eq(0), 42 | bad.eq(0), 43 | ), 44 | If(sink.valid, 45 | If(prbs.o == sink.data, 46 | good.eq(good + 1) 47 | ).Else( 48 | bad.eq(bad + 1) 49 | ), 50 | ), 51 | ] 52 | 53 | self.comb += [ 54 | sink.ready.eq(1), 55 | self.good.status.eq(good), 56 | self.bad.status.eq(bad), 57 | 58 | prbs.ce.eq(sink.valid), 59 | prbs.reset.eq(self.reset.re) 60 | ] 61 | 62 | 63 | class PRBSStream(Module, AutoCSR): 64 | def __init__(self): 65 | self.submodules.source = PRBSSource() 66 | self.submodules.sink = PRBSSink() 67 | -------------------------------------------------------------------------------- /gateware/rtl/reboot.py: -------------------------------------------------------------------------------- 1 | from migen import Module, Signal, If, Instance 2 | from litex.soc.interconnect.csr import AutoCSR, CSRStorage 3 | 4 | class Reboot(Module, AutoCSR): 5 | def __init__(self, rst, ext_rst=None): 6 | 7 | self.ctrl = CSRStorage(8) 8 | 9 | do_reset = Signal() 10 | self.comb += [ 11 | # "Reset Key" is 0xac (0b10101100) 12 | do_reset.eq(self.ctrl.storage[2] & self.ctrl.storage[3] & ~self.ctrl.storage[4] 13 | & self.ctrl.storage[5] & ~self.ctrl.storage[6] & self.ctrl.storage[7]) 14 | ] 15 | 16 | reset_latch = Signal(reset=0) 17 | if ext_rst is None: 18 | self.sync += [ 19 | reset_latch.eq(do_reset | reset_latch) 20 | ] 21 | else: 22 | self.sync += [ 23 | reset_latch.eq(do_reset | reset_latch | ext_rst) 24 | ] 25 | 26 | self.comb += [ 27 | rst.eq(~reset_latch) 28 | ] 29 | 30 | -------------------------------------------------------------------------------- /gateware/rtl/rgb_led.py: -------------------------------------------------------------------------------- 1 | 2 | from migen import * 3 | 4 | from litex.soc.interconnect.csr import AutoCSR, CSRStorage, CSRField 5 | 6 | class PWM(Module): 7 | def __init__(self, bitwidth, tick, offset): 8 | self.out = pwm = Signal(1) 9 | pwm_counter = Signal(bitwidth) 10 | counter_value = Signal(8) 11 | counter = Signal(16) 12 | 13 | # Sine Modulation 14 | self.specials.mem = Memory(16, 256, init=self.gen_gamma_table(256)) 15 | p = self.mem.get_port() 16 | self.specials += p 17 | self.comb += p.adr.eq(counter_value) 18 | self.comb += counter.eq(p.dat_r) 19 | 20 | self.sync += [ 21 | If(tick, 22 | counter_value.eq(counter_value + 1) 23 | ) 24 | ] 25 | 26 | self.comb += pwm.eq(pwm_counter < counter) 27 | self.sync += pwm_counter.eq(pwm_counter + 1) 28 | 29 | def gen_gamma_table(self, n): 30 | from math import sin,pi 31 | return [int(0x7FFF * (sin(((2*pi) / 255.0) * i) + 1.0)) for i in range(n)] 32 | 33 | 34 | 35 | class PDM(Module): 36 | def __init__(self, width=16): 37 | self.level = level = Signal(8) 38 | level_corr = Signal(16) 39 | self.out = out = Signal(1) 40 | 41 | # Gamma correction 42 | self.specials.mem = Memory(16, 256, init=self.gen_gamma_table(256)) 43 | p = self.mem.get_port() 44 | self.specials += p 45 | self.comb += p.adr.eq(level) 46 | self.comb += level_corr.eq(p.dat_r) 47 | 48 | sigma = Signal(width+1) 49 | 50 | self.comb += out.eq(sigma[width]) 51 | self.sync += sigma.eq(sigma + Cat(level_corr, out, out)) 52 | 53 | def gen_gamma_table(self, n): 54 | gamma = 1.5 55 | return [int(0xFFFF * pow((1.0 / 255.0) * i, gamma)) for i in range(n)] 56 | 57 | class RGB(Module, AutoCSR): 58 | def __init__(self, rgb_pins): 59 | 60 | 61 | self._r = CSRStorage(8) 62 | self._g = CSRStorage(8, reset=255) 63 | self._b = CSRStorage(8) 64 | 65 | 66 | self._div_m = CSRStorage(32) 67 | 68 | self._config = CSRStorage(2, fields=[ 69 | CSRField('breath', size=1, description='Modulate output with a breath effect'), 70 | CSRField('rainbow', size=1, description='Modulate output with rainbow'), 71 | ]) 72 | 73 | div_m_counter = Signal(32) 74 | strobe = Signal() 75 | 76 | 77 | from PIL import Image 78 | im = Image.open('util/colours.png') 79 | x,_ = im.size 80 | colours = [] 81 | for i in range(x): 82 | p_r,p_g,p_b = im.getpixel((i,0)) 83 | colours += [(p_r << 16) | (p_g << 8) | (p_b)] 84 | 85 | rainbow_index = Signal(x) 86 | self.specials.mem = Memory(24, x, init=colours) 87 | p = self.mem.get_port() 88 | self.specials += p 89 | self.sync += p.adr.eq(rainbow_index) 90 | self.autocsr_exclude = ['mem'] 91 | 92 | 93 | self.sync += [ 94 | If(div_m_counter >= self._div_m.storage, 95 | div_m_counter.eq(0), 96 | rainbow_index.eq(rainbow_index + 1) 97 | ).Else( 98 | div_m_counter.eq(div_m_counter + 1) 99 | ) 100 | ] 101 | 102 | # Activate strobe on counter rollover if counter is enabled. 103 | self.comb += strobe.eq((div_m_counter == 0) & (self._div_m.storage != 0)) 104 | 105 | self.submodules.pdm_r = PDM(16) 106 | self.submodules.pdm_g = PDM(16) 107 | self.submodules.pdm_b = PDM(16) 108 | 109 | self.submodules.pwm0 = PWM(16, strobe, 1 << 0) 110 | 111 | modulate = Signal() 112 | self.comb += modulate.eq(Mux(self._config.fields.breath, self.pwm0.out, 1)) 113 | 114 | self.comb += [ 115 | rgb_pins.r.eq(~(self.pdm_r.out & modulate)), 116 | rgb_pins.g.eq(~(self.pdm_g.out & modulate)), 117 | rgb_pins.b.eq(~(self.pdm_b.out & modulate)), 118 | ] 119 | 120 | self.comb += [ 121 | If(self._config.fields.rainbow, 122 | self.pdm_r.level.eq(p.dat_r[16:24]), 123 | self.pdm_g.level.eq(p.dat_r[8:16]), 124 | self.pdm_b.level.eq(p.dat_r[0:8]) 125 | ).Else( 126 | self.pdm_r.level.eq(self._r.storage), 127 | self.pdm_g.level.eq(self._g.storage), 128 | self.pdm_b.level.eq(self._b.storage) 129 | ) 130 | ] -------------------------------------------------------------------------------- /gateware/rtl/streamable_hyperram.py: -------------------------------------------------------------------------------- 1 | from migen import * 2 | 3 | from litex.soc.interconnect.csr import AutoCSR, CSR, CSRStatus, CSRStorage 4 | from litex.soc.interconnect.stream import Endpoint, EndpointDescription, SyncFIFO, AsyncFIFO, Monitor 5 | from litex.soc.interconnect.wishbone import InterconnectShared, Arbiter, SRAM, InterconnectPointToPoint, Interface 6 | from migen.genlib.cdc import PulseSynchronizer, MultiReg, BusSynchronizer 7 | 8 | from litehyperbus.core.hyperram_ddrx2 import HyperRAMX2 9 | 10 | 11 | class StreamableHyperRAM(Module, AutoCSR): 12 | def __init__(self, hyperram_pads, devices=[]): 13 | self.bus = cpu_bus = Interface() 14 | 15 | self.submodules.hyperram = hyperram = HyperRAMX2(hyperram_pads) 16 | devices = [d.bus for d in devices] 17 | 18 | self.submodules.arbiter = Arbiter(devices + [cpu_bus], hyperram.bus) 19 | 20 | # Analyser signals for debug 21 | self.dbg = hyperram.dbg 22 | 23 | # CSRs for adjusting IO delays 24 | self.io_loadn = CSRStorage() 25 | self.io_move = CSRStorage() 26 | self.io_direction = CSRStorage() 27 | self.clk_loadn = CSRStorage() 28 | self.clk_move = CSRStorage() 29 | self.clk_direction = CSRStorage() 30 | 31 | self.comb += [ 32 | hyperram.dly_io.loadn.eq(self.io_loadn.storage), 33 | hyperram.dly_io.move.eq(self.io_move.storage), 34 | hyperram.dly_io.direction.eq(self.io_direction.storage), 35 | 36 | hyperram.dly_clk.loadn.eq(self.clk_loadn.storage), 37 | hyperram.dly_clk.move.eq(self.clk_move.storage), 38 | hyperram.dly_clk.direction.eq(self.clk_direction.storage), 39 | ] 40 | 41 | -------------------------------------------------------------------------------- /gateware/rtl/verilog/fake_differential.v: -------------------------------------------------------------------------------- 1 | // DDR mode uses Lattice ECP5 vendor-specific module ODDRX1F 2 | 3 | module fake_differential 4 | ( 5 | input clk_shift, // used only in DDR mode 6 | // [1:0]:DDR [0]:SDR TMDS 7 | input [1:0] in_clock, in_red, in_green, in_blue, 8 | // [3]:clock [2]:red [1]:green [0]:blue 9 | output [3:0] out_p, 10 | 11 | input [3:0] move, 12 | input loadn, 13 | input dir 14 | ); 15 | parameter C_ddr = 1'b0; // 0:SDR 1:DDR 16 | 17 | wire [1:0] tmds[3:0]; 18 | assign tmds[3] = ~in_clock; 19 | assign tmds[2] = ~in_red; 20 | assign tmds[1] = in_green; 21 | assign tmds[0] = ~in_blue; 22 | 23 | // register stage to improve timing of the fake differential 24 | reg [1:0] R_tmds_p[3:0]; 25 | generate 26 | genvar i; 27 | for(i = 0; i < 4; i++) 28 | begin : TMDS_pn_registers 29 | always @(posedge clk_shift) R_tmds_p[i] <= tmds[i]; 30 | end 31 | endgenerate 32 | 33 | wire [3:0] _out_p; 34 | 35 | // output SDR/DDR to fake differential 36 | generate 37 | genvar i; 38 | if(C_ddr == 1'b1) 39 | for(i = 0; i < 4; i++) 40 | begin : DDR_output_mode 41 | ODDRX1F 42 | ddr_p_instance 43 | ( 44 | .D0(R_tmds_p[i][0]), 45 | .D1(R_tmds_p[i][1]), 46 | .Q(_out_p[i]), 47 | .SCLK(clk_shift), 48 | .RST(0) 49 | ); 50 | DELAYF 51 | #( 52 | .DEL_MODE("USER_DEFINED"), 53 | .DEL_VALUE(0) 54 | ) 55 | del_p 56 | ( 57 | .A(_out_p[i]), 58 | .LOADN(loadn), 59 | .MOVE(move[i]), 60 | .DIRECTION(dir), 61 | .Z(out_p[i]) 62 | ); 63 | end 64 | else 65 | for(i = 0; i < 4; i++) 66 | begin : SDR_output_mode 67 | assign out_p[i] = R_tmds_p[i][0]; 68 | end 69 | endgenerate 70 | 71 | endmodule 72 | -------------------------------------------------------------------------------- /gateware/rtl/verilog/tmds_encoder.v: -------------------------------------------------------------------------------- 1 | // File hdl/tmds_encoder.vhd translated with vhd2vl v3.0 VHDL to Verilog RTL translator 2 | // vhd2vl settings: 3 | // * Verilog Module Declaration Style: 2001 4 | 5 | // vhd2vl is Free (libre) Software: 6 | // Copyright (C) 2001 Vincenzo Liguori - Ocean Logic Pty Ltd 7 | // http://www.ocean-logic.com 8 | // Modifications Copyright (C) 2006 Mark Gonzales - PMC Sierra Inc 9 | // Modifications (C) 2010 Shankar Giri 10 | // Modifications Copyright (C) 2002-2017 Larry Doolittle 11 | // http://doolittle.icarus.com/~larry/vhd2vl/ 12 | // Modifications (C) 2017 Rodrigo A. Melo 13 | // 14 | // vhd2vl comes with ABSOLUTELY NO WARRANTY. Always check the resulting 15 | // Verilog for correctness, ideally with a formal verification tool. 16 | // 17 | // You are welcome to redistribute vhd2vl under certain conditions. 18 | // See the license (GPLv2) file included with the source for details. 19 | 20 | // The result of translation follows. Its copyright status should be 21 | // considered unchanged from the original VHDL. 22 | 23 | //-------------------------------------------------------------------------------- 24 | // Engineer: Mike Field 25 | // 26 | // Description: TMDS Encoder 27 | // 8 bits colour, 2 control bits and one blanking bits in 28 | // 10 bits of TMDS encoded data out 29 | // Clocked at the pixel clock 30 | // 31 | //-------------------------------------------------------------------------------- 32 | // See: http://hamsterworks.co.nz/mediawiki/index.php/Dvid_test 33 | // http://hamsterworks.co.nz/mediawiki/index.php/FPGA_Projects 34 | // 35 | // Copyright (c) 2012 Mike Field 36 | // 37 | // Permission is hereby granted, free of charge, to any person obtaining a copy 38 | // of this software and associated documentation files (the "Software"), to deal 39 | // in the Software without restriction, including without limitation the rights 40 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 41 | // copies of the Software, and to permit persons to whom the Software is 42 | // furnished to do so, subject to the following conditions: 43 | // 44 | // The above copyright notice and this permission notice shall be included in 45 | // all copies or substantial portions of the Software. 46 | // 47 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 48 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 49 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 50 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 51 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 52 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 53 | // THE SOFTWARE. 54 | // 55 | // no timescale needed 56 | 57 | module tmds_encoder( 58 | input wire clk, 59 | input wire [7:0] data, 60 | input wire [1:0] c, 61 | input wire blank, 62 | output reg [9:0] encoded 63 | ); 64 | 65 | 66 | 67 | 68 | wire [8:0] xored; 69 | wire [8:0] xnored; 70 | wire [3:0] ones; 71 | reg [8:0] data_word; 72 | reg [8:0] data_word_inv; 73 | wire [3:0] data_word_disparity; 74 | reg [3:0] dc_bias = 1'b0; 75 | 76 | // Work our the two different encodings for the byte 77 | assign xored[0] = data[0]; 78 | assign xored[1] = data[1] ^ xored[0]; 79 | assign xored[2] = data[2] ^ xored[1]; 80 | assign xored[3] = data[3] ^ xored[2]; 81 | assign xored[4] = data[4] ^ xored[3]; 82 | assign xored[5] = data[5] ^ xored[4]; 83 | assign xored[6] = data[6] ^ xored[5]; 84 | assign xored[7] = data[7] ^ xored[6]; 85 | assign xored[8] = 1'b1; 86 | assign xnored[0] = data[0]; 87 | assign xnored[1] = ~(data[1] ^ xnored[0]); 88 | assign xnored[2] = ~(data[2] ^ xnored[1]); 89 | assign xnored[3] = ~(data[3] ^ xnored[2]); 90 | assign xnored[4] = ~(data[4] ^ xnored[3]); 91 | assign xnored[5] = ~(data[5] ^ xnored[4]); 92 | assign xnored[6] = ~(data[6] ^ xnored[5]); 93 | assign xnored[7] = ~(data[7] ^ xnored[6]); 94 | assign xnored[8] = 1'b0; 95 | // Count how many ones are set in data 96 | assign ones = 4'b0000 + data[0] + data[1] + data[2] + data[3] + data[4] + data[5] + data[6] + data[7]; 97 | // Decide which encoding to use 98 | always @(ones, data[0], xnored, xored) begin 99 | if(ones > 4 || (ones == 4 && data[0] == 1'b0)) begin 100 | data_word <= xnored; 101 | data_word_inv <= ~(xnored); 102 | end 103 | else begin 104 | data_word <= xored; 105 | data_word_inv <= ~(xored); 106 | end 107 | end 108 | 109 | // Work out the DC bias of the dataword; 110 | assign data_word_disparity = 4'b1100 + data_word[0] + data_word[1] + data_word[2] + data_word[3] + data_word[4] + data_word[5] + data_word[6] + data_word[7]; 111 | // Now work out what the output should be 112 | always @(posedge clk) begin 113 | if(blank == 1'b1) begin 114 | // In the control periods, all values have and have balanced bit count 115 | case(c) 116 | 2'b00 : begin 117 | encoded <= 10'b1101010100; 118 | end 119 | 2'b01 : begin 120 | encoded <= 10'b0010101011; 121 | end 122 | 2'b10 : begin 123 | encoded <= 10'b0101010100; 124 | end 125 | default : begin 126 | encoded <= 10'b1010101011; 127 | end 128 | endcase 129 | dc_bias <= {4{1'b0}}; 130 | end 131 | else begin 132 | if(dc_bias == 5'b00000 || data_word_disparity == 0) begin 133 | // dataword has no disparity 134 | if(data_word[8] == 1'b1) begin 135 | encoded <= {2'b01,data_word[7:0]}; 136 | dc_bias <= dc_bias + data_word_disparity; 137 | end 138 | else begin 139 | encoded <= {2'b10,data_word_inv[7:0]}; 140 | dc_bias <= dc_bias - data_word_disparity; 141 | end 142 | end 143 | else if((dc_bias[3] == 1'b0 && data_word_disparity[3] == 1'b0) || (dc_bias[3] == 1'b1 && data_word_disparity[3] == 1'b1)) begin 144 | encoded <= {1'b1,data_word[8],data_word_inv[7:0]}; 145 | dc_bias <= dc_bias + data_word[8] - data_word_disparity; 146 | end 147 | else begin 148 | encoded <= {1'b0,data_word}; 149 | dc_bias <= dc_bias - data_word_inv[8] + data_word_disparity; 150 | end 151 | end 152 | end 153 | 154 | 155 | endmodule 156 | -------------------------------------------------------------------------------- /gateware/rtl/verilog/vga2dvid.v: -------------------------------------------------------------------------------- 1 | // File hdl/vga2dvid.vhd translated with vhd2vl v3.0 VHDL to Verilog RTL translator 2 | // vhd2vl settings: 3 | // * Verilog Module Declaration Style: 2001 4 | 5 | // vhd2vl is Free (libre) Software: 6 | // Copyright (C) 2001 Vincenzo Liguori - Ocean Logic Pty Ltd 7 | // http://www.ocean-logic.com 8 | // Modifications Copyright (C) 2006 Mark Gonzales - PMC Sierra Inc 9 | // Modifications (C) 2010 Shankar Giri 10 | // Modifications Copyright (C) 2002-2017 Larry Doolittle 11 | // http://doolittle.icarus.com/~larry/vhd2vl/ 12 | // Modifications (C) 2017 Rodrigo A. Melo 13 | // 14 | // vhd2vl comes with ABSOLUTELY NO WARRANTY. Always check the resulting 15 | // Verilog for correctness, ideally with a formal verification tool. 16 | // 17 | // You are welcome to redistribute vhd2vl under certain conditions. 18 | // See the license (GPLv2) file included with the source for details. 19 | 20 | // The result of translation follows. Its copyright status should be 21 | // considered unchanged from the original VHDL. 22 | 23 | //------------------------------------------------------------------------------ 24 | // Engineer: Mike Field 25 | // Description: Converts VGA signals into DVID bitstreams. 26 | // 27 | // 'clk_shift' 10x clk_pixel for SDR 28 | // 'clk_shift' 5x clk_pixel for DDR 29 | // 30 | // 'blank' should be asserted during the non-display 31 | // portions of the frame 32 | //------------------------------------------------------------------------------ 33 | // See: http://hamsterworks.co.nz/mediawiki/index.php/Dvid_test 34 | // http://hamsterworks.co.nz/mediawiki/index.php/FPGA_Projects 35 | // 36 | // Copyright (c) 2012 Mike Field 37 | // 38 | // Permission is hereby granted, free of charge, to any person obtaining a copy 39 | // of this software and associated documentation files (the "Software"), to deal 40 | // in the Software without restriction, including without limitation the rights 41 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 42 | // copies of the Software, and to permit persons to whom the Software is 43 | // furnished to do so, subject to the following conditions: 44 | // 45 | // The above copyright notice and this permission notice shall be included in 46 | // all copies or substantial portions of the Software. 47 | // 48 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 54 | // THE SOFTWARE. 55 | // 56 | // takes VGA input and prepares output 57 | // for SDR buffer, which send 1 bit per 1 clock period output out_red(0), out_green(0), ... etc. 58 | // for DDR buffers, which send 2 bits per 1 clock period output out_red(1 downto 0), ... 59 | // EMARD unified SDR and DDR into one module 60 | // no timescale needed 61 | 62 | module vga2dvid( 63 | input wire clk_pixel, 64 | input wire clk_shift, 65 | input wire [8 - 1:0] in_red, 66 | input wire [8 - 1:0] in_green, 67 | input wire [8 - 1:0] in_blue, 68 | input wire in_blank, 69 | input wire in_hsync, 70 | input wire in_vsync, 71 | output wire [9:0] outp_red, 72 | output wire [9:0] outp_green, 73 | output wire [9:0] outp_blue, 74 | output wire [1:0] out_red, 75 | output wire [1:0] out_green, 76 | output wire [1:0] out_blue, 77 | output wire [1:0] out_clock 78 | ); 79 | 80 | parameter C_shift_clock_synchronizer=1'b1; 81 | parameter C_parallel=1'b1; 82 | parameter C_serial=1'b1; 83 | parameter C_ddr=1'b0; 84 | parameter [31:0] C_depth=8; 85 | // VGA pixel clock, 25 MHz for 640x480 86 | // SDR: 10x clk_pixel, DDR: 5x clk_pixel, in phase with clk_pixel 87 | // parallel outputs 88 | // serial outputs 89 | 90 | 91 | 92 | wire [9:0] encoded_red; wire [9:0] encoded_green; wire [9:0] encoded_blue; 93 | reg [9:0] latched_red = 1'b0; reg [9:0] latched_green = 1'b0; reg [9:0] latched_blue = 1'b0; 94 | reg [9:0] shift_red = 1'b0; reg [9:0] shift_green = 1'b0; reg [9:0] shift_blue = 1'b0; 95 | parameter C_shift_clock_initial = 10'b0000011111; 96 | reg [9:0] shift_clock = C_shift_clock_initial; 97 | reg R_shift_clock_off_sync = 1'b0; 98 | reg [7:0] R_shift_clock_synchronizer = 1'b1; 99 | reg [6:0] R_sync_fail; // counts sync fails, after too many, reinitialize shift_clock 100 | parameter c_red = 1'b0; 101 | parameter c_green = 1'b0; 102 | wire [1:0] c_blue; 103 | wire [7:0] red_d; 104 | wire [7:0] green_d; 105 | wire [7:0] blue_d; 106 | 107 | assign c_blue = {in_vsync,in_hsync}; 108 | assign red_d[7:8 - C_depth] = in_red[C_depth - 1:0]; 109 | assign green_d[7:8 - C_depth] = in_green[C_depth - 1:0]; 110 | assign blue_d[7:8 - C_depth] = in_blue[C_depth - 1:0]; 111 | // fill vacant low bits with value repeated (so min/max value is always 0 or 255) 112 | genvar i; 113 | generate for (i=8 - C_depth - 1; i >= 0; i = i - 1) begin: G_bits 114 | // red_d(i) <= in_red(0); 115 | // green_d(i) <= in_green(0); 116 | // blue_d(i) <= in_blue(0); 117 | end 118 | endgenerate 119 | generate if (C_shift_clock_synchronizer == 1'b1) begin: G_shift_clock_synchronizer 120 | // sampler verifies is shift_clock state synchronous with pixel_clock 121 | always @(posedge clk_pixel) begin 122 | // does 0 to 1 transition at bits 5 downto 4 happen at rising_edge of clk_pixel? 123 | // if shift_clock = C_shift_clock_initial then 124 | if(shift_clock[5:4] == C_shift_clock_initial[5:4]) begin 125 | // same as above line but simplified 126 | R_shift_clock_off_sync <= 1'b0; 127 | end 128 | else begin 129 | R_shift_clock_off_sync <= 1'b1; 130 | end 131 | end 132 | 133 | // every N cycles of clk_shift: signal to skip 1 cycle in order to get in sync 134 | always @(posedge clk_shift) begin 135 | if(R_shift_clock_off_sync == 1'b1) begin 136 | if(R_shift_clock_synchronizer[(7)] == 1'b1) begin 137 | R_shift_clock_synchronizer <= {8{1'b0}}; 138 | end 139 | else begin 140 | R_shift_clock_synchronizer <= R_shift_clock_synchronizer + 1; 141 | end 142 | end 143 | else begin 144 | R_shift_clock_synchronizer <= {8{1'b0}}; 145 | end 146 | end 147 | 148 | end 149 | endgenerate 150 | // shift_clock_synchronizer 151 | tmds_encoder u21( 152 | .clk(clk_pixel), 153 | .data(red_d), 154 | .c(c_red), 155 | .blank(in_blank), 156 | .encoded(encoded_red)); 157 | 158 | tmds_encoder u22( 159 | .clk(clk_pixel), 160 | .data(green_d), 161 | .c(c_green), 162 | .blank(in_blank), 163 | .encoded(encoded_green)); 164 | 165 | tmds_encoder u23( 166 | .clk(clk_pixel), 167 | .data(blue_d), 168 | .c(c_blue), 169 | .blank(in_blank), 170 | .encoded(encoded_blue)); 171 | 172 | always @(posedge clk_pixel) begin 173 | latched_red <= encoded_red; 174 | latched_green <= encoded_green; 175 | latched_blue <= encoded_blue; 176 | end 177 | 178 | generate if (C_parallel == 1'b1) begin: G_parallel 179 | assign outp_red = latched_red; 180 | assign outp_green = latched_green; 181 | assign outp_blue = latched_blue; 182 | end 183 | endgenerate 184 | 185 | generate if ((C_serial & C_ddr) == 1'b1) begin: G_DDR 186 | always @(posedge clk_shift) begin 187 | //if shift_clock = "0000011111" then 188 | if(shift_clock[5:4] == C_shift_clock_initial[5:4]) begin 189 | // same as above line but simplified 190 | shift_red <= latched_red; 191 | shift_green <= latched_green; 192 | shift_blue <= latched_blue; 193 | 194 | end 195 | else begin 196 | shift_red <= {2'b00,shift_red[9:2]}; 197 | shift_green <= {2'b00,shift_green[9:2]}; 198 | shift_blue <= {2'b00,shift_blue[9:2]}; 199 | end 200 | if(R_shift_clock_synchronizer[(7)] == 1'b0) begin 201 | shift_clock <= {shift_clock[1:0],shift_clock[9:2]}; 202 | end 203 | else begin 204 | // synchronization failed. 205 | // after too many fails, reinitialize shift_clock 206 | if(R_sync_fail[(6)] == 1'b1) begin 207 | shift_clock <= C_shift_clock_initial; 208 | R_sync_fail <= {7{1'b0}}; 209 | end 210 | else begin 211 | R_sync_fail <= R_sync_fail + 1; 212 | end 213 | end 214 | end 215 | 216 | end 217 | endgenerate 218 | // SDR: use only bit 0 from each out_* channel 219 | // DDR: 2 bits per 1 clock period, 220 | // (one bit output on rising edge, other on falling edge of clk_shift) 221 | generate if (C_serial == 1'b1) begin: G_serial 222 | assign out_red = shift_red[1:0]; 223 | assign out_green = shift_green[1:0]; 224 | assign out_blue = shift_blue[1:0]; 225 | assign out_clock = shift_clock[1:0]; 226 | end 227 | endgenerate 228 | 229 | endmodule 230 | -------------------------------------------------------------------------------- /gateware/rtl/video/YCrCb.py: -------------------------------------------------------------------------------- 1 | # ycbcr2rgb 2 | 3 | from migen import * 4 | 5 | from litex.soc.interconnect.stream import * 6 | 7 | def saturate(i, o, minimum, maximum): 8 | return [ 9 | If(i > maximum, 10 | o.eq(maximum) 11 | ).Elif(i < minimum, 12 | o.eq(minimum) 13 | ).Else( 14 | o.eq(i) 15 | ) 16 | ] 17 | 18 | 19 | def coef(value, cw=None): 20 | return int(value * 2**cw) if cw is not None else value 21 | 22 | def rgb_layout(dw): 23 | return [("r", dw), ("g", dw), ("b", dw)] 24 | 25 | def rgb16f_layout(dw): 26 | return [("rf", dw), ("gf", dw), ("bf", dw)] 27 | 28 | def ycbcr444_layout(dw): 29 | return [("y", dw), ("cb", dw), ("cr", dw)] 30 | 31 | def ycbcr422_layout(dw): 32 | return [("y", dw), ("cb_cr", dw)] 33 | 34 | def pix_layout(dw): 35 | return [("pix", dw)] 36 | 37 | def pixf_layout(dw): 38 | return [("pixf", dw)] 39 | 40 | def ycbcr2rgb_coefs(dw, cw=None): 41 | ca = 0.1819 42 | cb = 0.0618 43 | cc = 0.5512 44 | cd = 0.6495 45 | xcoef_w = None if cw is None else cw-2 46 | return { 47 | "ca" : coef(ca, cw), 48 | "cb" : coef(cb, cw), 49 | "cc" : coef(cc, cw), 50 | "cd" : coef(cd, cw), 51 | "yoffset" : 2**(dw-4), 52 | "coffset" : 2**(dw-1), 53 | "ymax" : 2**dw-1, 54 | "cmax" : 2**dw-1, 55 | "ymin" : 0, 56 | "cmin" : 0, 57 | "acoef": coef(1/cd, xcoef_w), 58 | "bcoef": coef(-cb/(cc*(1-ca-cb)), xcoef_w), 59 | "ccoef": coef(-ca/(cd*(1-ca-cb)), xcoef_w), 60 | "dcoef": coef(1/cc, xcoef_w) 61 | } 62 | 63 | 64 | @CEInserter() 65 | class YCbCr2RGBDatapath(Module): 66 | latency = 4 67 | 68 | def __init__(self, ycbcr_w, rgb_w, coef_w): 69 | self.sink = sink = Record(ycbcr444_layout(ycbcr_w)) 70 | self.source = source = Record(rgb_layout(rgb_w)) 71 | 72 | # # # 73 | 74 | coefs = ycbcr2rgb_coefs(rgb_w, coef_w) 75 | 76 | # delay ycbcr signals 77 | ycbcr_delayed = [sink] 78 | for i in range(self.latency): 79 | ycbcr_n = Record(ycbcr444_layout(ycbcr_w)) 80 | for name in ["y", "cb", "cr"]: 81 | self.sync += getattr(ycbcr_n, name).eq(getattr(ycbcr_delayed[-1], name)) 82 | ycbcr_delayed.append(ycbcr_n) 83 | 84 | # Hardware implementation: 85 | # (Equation from XAPP931) 86 | # r = y - yoffset + (cr - coffset)*acoef 87 | # b = y - yoffset + (cb - coffset)*bcoef + (cr - coffset)*ccoef 88 | # g = y - yoffset + (cb - coffset)*dcoef 89 | 90 | # stage 1 91 | # (cr - coffset) & (cr - coffset) 92 | cb_minus_coffset = Signal((ycbcr_w + 1, True)) 93 | cr_minus_coffset = Signal((ycbcr_w + 1, True)) 94 | self.sync += [ 95 | cb_minus_coffset.eq(sink.cb - coefs["coffset"]), 96 | cr_minus_coffset.eq(sink.cr - coefs["coffset"]) 97 | ] 98 | 99 | # stage 2 100 | # (y - yoffset) 101 | # (cr - coffset)*acoef 102 | # (cb - coffset)*bcoef 103 | # (cr - coffset)*ccoef 104 | # (cb - coffset)*dcoef 105 | y_minus_yoffset = Signal((ycbcr_w + 4, True)) 106 | cr_minus_coffset_mult_acoef = Signal((ycbcr_w + coef_w + 4, True)) 107 | cb_minus_coffset_mult_bcoef = Signal((ycbcr_w + coef_w + 4, True)) 108 | cr_minus_coffset_mult_ccoef = Signal((ycbcr_w + coef_w + 4, True)) 109 | cb_minus_coffset_mult_dcoef = Signal((ycbcr_w + coef_w + 4, True)) 110 | self.sync += [ 111 | y_minus_yoffset.eq(ycbcr_delayed[1].y - coefs["yoffset"]), 112 | cr_minus_coffset_mult_acoef.eq(cr_minus_coffset * Signal((ycbcr_w, True), reset=coefs["acoef"] )), 113 | cb_minus_coffset_mult_bcoef.eq(cb_minus_coffset * Signal((ycbcr_w, True), reset=coefs["bcoef"] )), 114 | cr_minus_coffset_mult_ccoef.eq(cr_minus_coffset * Signal((ycbcr_w, True), reset=coefs["ccoef"] )), 115 | cb_minus_coffset_mult_dcoef.eq(cb_minus_coffset * Signal((ycbcr_w, True), reset=coefs["dcoef"] )) 116 | ] 117 | 118 | # stage 3 119 | # line addition for all component 120 | r = Signal((ycbcr_w + 4, True)) 121 | g = Signal((ycbcr_w + 4, True)) 122 | b = Signal((ycbcr_w + 4, True)) 123 | self.sync += [ 124 | r.eq(y_minus_yoffset + cr_minus_coffset_mult_acoef[coef_w-2:]), 125 | g.eq(y_minus_yoffset + cb_minus_coffset_mult_bcoef[coef_w-2:] + 126 | cr_minus_coffset_mult_ccoef[coef_w-2:]), 127 | b.eq(y_minus_yoffset + cb_minus_coffset_mult_dcoef[coef_w-2:]) 128 | ] 129 | 130 | # stage 4 131 | # saturate 132 | self.sync += [ 133 | saturate(r, source.r, 0, 2**rgb_w-1), 134 | saturate(g, source.g, 0, 2**rgb_w-1), 135 | saturate(b, source.b, 0, 2**rgb_w-1) 136 | ] 137 | 138 | 139 | class YCbCr2RGB(PipelinedActor, Module): 140 | def __init__(self, ycbcr_w=8, rgb_w=8, coef_w=8): 141 | self.sink = sink = Endpoint(EndpointDescription(ycbcr444_layout(ycbcr_w))) 142 | self.source = source = Endpoint(EndpointDescription(rgb_layout(rgb_w))) 143 | 144 | # # # 145 | 146 | self.submodules.datapath = YCbCr2RGBDatapath(ycbcr_w, rgb_w, coef_w) 147 | PipelinedActor.__init__(self, self.datapath.latency) 148 | self.comb += self.datapath.ce.eq(self.pipe_ce) 149 | for name in ["y", "cb", "cr"]: 150 | self.comb += getattr(self.datapath.sink, name).eq(getattr(sink, name)) 151 | for name in ["r", "g", "b"]: 152 | self.comb += getattr(source, name).eq(getattr(self.datapath.source, name)) 153 | 154 | 155 | 156 | 157 | @ResetInserter() 158 | class YCbCr422to444(Module): 159 | """YCbCr 422 to 444 160 | 161 | Input: Output: 162 | Y0 Y1 Y2 Y3 Y0 Y1 Y2 Y3 163 | Cb01 Cr01 Cb23 Cr23 --> Cb01 Cb01 Cb23 Cb23 164 | Cr01 Cr01 Cr23 Cr23 165 | """ 166 | latency = 2 167 | def __init__(self, dw=8): 168 | self.sink = sink = Endpoint(EndpointDescription(ycbcr422_layout(dw))) 169 | self.source = source = Endpoint(EndpointDescription(ycbcr444_layout(dw))) 170 | 171 | # # # 172 | 173 | y_fifo = SyncFIFO([("data", dw)], 4) 174 | cb_fifo = SyncFIFO([("data", dw)], 4) 175 | cr_fifo = SyncFIFO([("data", dw)], 4) 176 | self.submodules += y_fifo, cb_fifo, cr_fifo 177 | 178 | # input 179 | parity_in = Signal() 180 | self.sync += If(sink.valid & sink.ready, parity_in.eq(~parity_in)) 181 | self.comb += [ 182 | If(~parity_in, 183 | y_fifo.sink.valid.eq(sink.valid & sink.ready), 184 | y_fifo.sink.data.eq(sink.y), 185 | cb_fifo.sink.valid.eq(sink.valid & sink.ready), 186 | cb_fifo.sink.data.eq(sink.cb_cr), 187 | sink.ready.eq(y_fifo.sink.ready & cb_fifo.sink.ready) 188 | ).Else( 189 | y_fifo.sink.valid.eq(sink.valid & sink.ready), 190 | y_fifo.sink.data.eq(sink.y), 191 | cr_fifo.sink.valid.eq(sink.valid & sink.ready), 192 | cr_fifo.sink.data.eq(sink.cb_cr), 193 | sink.ready.eq(y_fifo.sink.ready & cr_fifo.sink.ready) 194 | ) 195 | ] 196 | 197 | 198 | # output 199 | parity_out = Signal() 200 | self.sync += If(source.valid & source.ready, parity_out.eq(~parity_out)) 201 | self.comb += [ 202 | source.valid.eq(y_fifo.source.valid & 203 | cb_fifo.source.valid & 204 | cr_fifo.source.valid), 205 | source.y.eq(y_fifo.source.data), 206 | source.cb.eq(cb_fifo.source.data), 207 | source.cr.eq(cr_fifo.source.data), 208 | y_fifo.source.ready.eq(source.valid & source.ready), 209 | cb_fifo.source.ready.eq(source.valid & source.ready & parity_out), 210 | cr_fifo.source.ready.eq(source.valid & source.ready & parity_out) 211 | ] 212 | -------------------------------------------------------------------------------- /gateware/rtl/video/boson.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright (c) 2020 Gregory Davill 2 | # License: BSD 3 | 4 | from migen import * 5 | 6 | from litex.soc.interconnect.stream import EndpointDescription, Endpoint 7 | 8 | from litex.soc.cores import uart 9 | from rtl.ecp5_dynamic_pll import period_ns 10 | 11 | from litex.soc.interconnect.csr import AutoCSR 12 | 13 | 14 | 15 | class BosonDataRx(Module): 16 | def __init__(self, pads): 17 | self.source = source = Endpoint(EndpointDescription([("data", 24)])) 18 | 19 | vsync_ = Signal() 20 | vsync_falling = Signal() 21 | 22 | pixel_counter = Signal(20) 23 | 24 | self.comb += [ 25 | vsync_falling.eq(~pads.vsync & vsync_), 26 | ] 27 | 28 | self.sync += [ 29 | source.data.eq(pads.data), 30 | source.valid.eq(pads.valid), 31 | 32 | vsync_.eq(pads.vsync), 33 | If(vsync_falling, 34 | pixel_counter.eq(0), 35 | ).Else( 36 | pixel_counter.eq(pixel_counter + 1) 37 | ) 38 | ] 39 | 40 | 41 | 42 | # Convert the Boson clock pin Signal into a clock domain 43 | class BosonClk(Module): 44 | def __init__(self, clk_pad, platform): 45 | self.clock_domains.cd_boson_rx = ClockDomain() 46 | self.comb += self.cd_boson_rx.clk.eq(clk_pad) 47 | 48 | platform.add_period_constraint(self.cd_boson_rx.clk, period_ns(27e6)) 49 | 50 | 51 | class Boson(Module, AutoCSR): 52 | def __init__(self, platform, pads, clk_freq): 53 | self.submodules.clk = BosonClk(pads.clk, platform) 54 | self.submodules.rx = ClockDomainsRenamer("boson_rx")(BosonDataRx(pads)) 55 | self.source = self.rx.source 56 | 57 | self.hsync = pads.hsync 58 | self.vsync = pads.vsync 59 | self.data_valid = pads.valid 60 | 61 | self.submodules.uart_phy = uart_phy = uart.UARTPHY(pads, clk_freq, baudrate=921600) 62 | self.submodules.uart = uart.UART(uart_phy, tx_fifo_depth=4, rx_fifo_depth=128) 63 | 64 | -------------------------------------------------------------------------------- /gateware/rtl/video/framer.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright (c) 2020 Gregory Davill 2 | # License: BSD 3 | 4 | from migen import * 5 | from litex.soc.interconnect.stream import Endpoint 6 | from rtl.edge_detect import EdgeDetect 7 | 8 | from litex.soc.interconnect.csr import AutoCSR, CSR, CSRStatus, CSRStorage 9 | from litex.soc.interconnect.stream import Endpoint, EndpointDescription, AsyncFIFO 10 | 11 | def framer_params(): 12 | return [ 13 | ("x_start", 16), 14 | ("y_start", 16), 15 | ("x_stop", 16), 16 | ("y_stop", 16), 17 | ] 18 | 19 | class Framer(Module, AutoCSR): 20 | def __init__(self): 21 | self.sink = sink = Endpoint([("data", 32)]) 22 | 23 | self.params = params = Endpoint(framer_params()) 24 | 25 | self.hsync = hsync = Signal() 26 | self.vsync = vsync = Signal() 27 | 28 | 29 | # VGA output 30 | self.red = red = Signal(8) 31 | self.green = green = Signal(8) 32 | self.blue = blue = Signal(8) 33 | self.data_valid = data_valid = Signal() 34 | 35 | # parameters 36 | pixel_counter = Signal(14) 37 | line_counter = Signal(14) 38 | 39 | h_det = EdgeDetect(mode="fall", input_cd="video", output_cd="video") 40 | v_det = EdgeDetect(mode="fall", input_cd="video", output_cd="video") 41 | self.submodules += h_det, v_det 42 | self.comb += [ 43 | h_det.i.eq(hsync), 44 | v_det.i.eq(vsync), 45 | ] 46 | 47 | self.comb += [ 48 | If((line_counter >= params.y_start) & (line_counter < params.y_stop), 49 | If((pixel_counter >= params.x_start) & (pixel_counter < params.x_stop), 50 | sink.ready.eq(1) 51 | ) 52 | ) 53 | ] 54 | 55 | self.sync.video += [ 56 | # Default values 57 | red.eq(0), 58 | green.eq(0), 59 | blue.eq(0), 60 | data_valid.eq(0), 61 | 62 | # Show pixels 63 | If((line_counter >= params.y_start) & (line_counter < params.y_stop), 64 | If((pixel_counter >= params.x_start) & (pixel_counter < params.x_stop), 65 | data_valid.eq(1), 66 | If(sink.valid, 67 | red.eq(sink.data[0:8]), 68 | green.eq(sink.data[8:16]), 69 | blue.eq(sink.data[16:24]) 70 | ).Else( 71 | red.eq(0xFF), 72 | green.eq(0x77), 73 | blue.eq(0xFF) 74 | ) 75 | ) 76 | ), 77 | 78 | # Horizontal timing for one line 79 | pixel_counter.eq(pixel_counter + 1), 80 | 81 | If(h_det.o, 82 | pixel_counter.eq(0), 83 | line_counter.eq(line_counter + 1), 84 | ), 85 | If(v_det.o, 86 | line_counter.eq(0), 87 | ) 88 | ] -------------------------------------------------------------------------------- /gateware/rtl/video/scaler_coeff_gen.py: -------------------------------------------------------------------------------- 1 | import math 2 | from numpy import arange 3 | 4 | 5 | def W(x): 6 | a = -0.5 7 | if abs(x) <= 1: 8 | return (a+2)*abs(x)**3 - (a+3)*abs(x)**2 + 1 9 | if abs(x) < 2: 10 | return (a)*abs(x)**3 - (5*a)*abs(x)**2 + 8*a*abs(x) - 4*a 11 | return 0 12 | 13 | 14 | def coeffs(i): 15 | return [W(n-i) for n in arange(-1.0,2.001,1)] 16 | 17 | 18 | 19 | 20 | 21 | def generate(delta_offset, n_phases, n_taps): 22 | output_coeffs = [] 23 | current_offset = 0 24 | total_offset = 0 25 | 26 | for n in range(n_phases): 27 | 28 | skip = False 29 | if current_offset + (delta_offset*2 + 0.0001) > 1.0: 30 | if not ((current_offset + delta_offset + 0.0001) > 1.0): 31 | skip = True 32 | 33 | c = coeffs(current_offset) 34 | def encode(d): 35 | return int(d * 2**8) 36 | 37 | for i in range(n_taps): 38 | v = encode(c[i]) & 0xFFFF 39 | 40 | if skip: 41 | v |= (1 << 31) 42 | 43 | v |= (i << 24) 44 | v |= (n << 16) 45 | 46 | 47 | output_coeffs += [v] 48 | 49 | total_offset += delta_offset 50 | current_offset += delta_offset 51 | if current_offset >= 1.0: 52 | current_offset -= 1.0 53 | 54 | return output_coeffs 55 | 56 | def print_c(coeff_list, name): 57 | chunks = [coeff_list[x:x+16] for x in range(0, len(coeff_list), 16)] 58 | 59 | print(f'const uint32_t {name} [{len(coeff_list)}] = {{') 60 | for c in chunks: 61 | print('\t', end='') 62 | for v in c: 63 | print("0x{0:08x}".format(v), end=', ') 64 | print('') 65 | print(f'}};\n\n') 66 | 67 | 68 | 69 | n_taps = 4 70 | delta_offset = 1.0 - 64/75 # (512 > 600) 71 | n_phases = 75 72 | 73 | 74 | 75 | output_coeffs = generate(delta_offset, n_phases, n_taps) 76 | print(f' // Scaler coefficients generated with the following parameters:') 77 | print(f' // delta_offset={delta_offset}') 78 | print(f' // n_phases={n_phases}') 79 | print(f' // n_taps={n_taps}') 80 | print(f'') 81 | print_c(output_coeffs, 'scaler_coef_512_600') 82 | 83 | 84 | 85 | delta_offset = 1.0 - 4/5 # (640 > 800) 86 | n_phases = 5 87 | 88 | 89 | output_coeffs = generate(delta_offset, n_phases, n_taps) 90 | print(f' // Scaler coefficients generated with the following parameters:') 91 | print(f' // delta_offset={delta_offset}') 92 | print(f' // n_phases={n_phases}') 93 | print(f' // n_taps={n_taps}') 94 | print(f'') 95 | print_c(output_coeffs, 'scaler_coef_640_800') 96 | 97 | 98 | 99 | 100 | delta_offset = 1.0 - 64/75 # (640 > 750) 101 | n_phases = 75 102 | 103 | 104 | output_coeffs = generate(delta_offset, n_phases, n_taps) 105 | print(f' // Scaler coefficients generated with the following parameters:') 106 | print(f' // delta_offset={delta_offset}') 107 | print(f' // n_phases={n_phases}') 108 | print(f' // n_taps={n_taps}') 109 | print(f'') 110 | print_c(output_coeffs, 'scaler_coef_640_750') 111 | 112 | 113 | 114 | 115 | delta_offset = 1.0 - 4/5 # (512 > 640) 116 | n_phases = 5 117 | 118 | 119 | output_coeffs = generate(delta_offset, n_phases, n_taps) 120 | print(f' // Scaler coefficients generated with the following parameters:') 121 | print(f' // delta_offset={delta_offset}') 122 | print(f' // n_phases={n_phases}') 123 | print(f' // n_taps={n_taps}') 124 | print(f'') 125 | print_c(output_coeffs, 'scaler_coef_512_640') 126 | 127 | 128 | 129 | 130 | 131 | delta_offset = 1.0 - 640/1280 # (512 > 640) 132 | n_phases = 5 133 | 134 | 512/720 135 | 136 | 137 | output_coeffs = generate(delta_offset, n_phases, n_taps) 138 | print(f' // Scaler coefficients generated with the following parameters:') 139 | print(f' // delta_offset={delta_offset}') 140 | print(f' // n_phases={n_phases}') 141 | print(f' // n_taps={n_taps}') 142 | print(f'') 143 | print_c(output_coeffs, 'scaler_coef_512_640') 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /gateware/rtl/video/simulated_video.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright (c) 2019 Frank Buss 2 | # This file is Copyright (c) 2020 Gregory Davill 3 | # License: BSD 4 | 5 | import os 6 | 7 | from migen import * 8 | 9 | from litex.soc.interconnect.csr import AutoCSR, CSR, CSRStatus, CSRStorage 10 | 11 | class SimulatedVideo(Module): 12 | def __init__(self): 13 | 14 | # Display resolution 15 | WIDTH = 640 16 | HEIGHT = 512 17 | 18 | # VGA output 19 | self.red = red = Signal(8) 20 | self.green = green = Signal(8) 21 | self.blue = blue = Signal(8) 22 | self.hsync = hsync = Signal() 23 | self.vsync = vsync = Signal() 24 | self.data_valid = data_valid = Signal() 25 | 26 | 27 | # VGA timings 28 | H_SYNC_PULSE = 8 29 | H_BACK_PORCH = 50 + H_SYNC_PULSE 30 | H_DATA = WIDTH + H_BACK_PORCH 31 | H_FRONT_PORCH = 52 + H_DATA 32 | # Total 750 clocks per row 33 | 34 | V_SYNC_PULSE = 88 35 | V_BACK_PORCH = 0 + V_SYNC_PULSE 36 | V_DATA = HEIGHT + V_BACK_PORCH 37 | V_FRONT_PORCH = 0 + V_DATA 38 | # 600 rows per frame. vsync LOW for 88 extra rows 39 | 40 | pixel_counter = Signal(14) 41 | line_counter = Signal(14) 42 | 43 | self.sync.pixel += [ 44 | # Default values 45 | red.eq(0), 46 | green.eq(0), 47 | blue.eq(0), 48 | data_valid.eq(0), 49 | 50 | # Show pixels 51 | If((line_counter >= V_BACK_PORCH) & (line_counter < V_DATA), 52 | If((pixel_counter >= H_BACK_PORCH) & (pixel_counter < (H_DATA)), 53 | data_valid.eq(1), 54 | red.eq(0x33), 55 | green.eq(0x33), 56 | blue.eq(0x33) 57 | ) 58 | ), 59 | 60 | 61 | 62 | # Horizontal timing for one line 63 | pixel_counter.eq(pixel_counter + 1), 64 | If(pixel_counter < H_SYNC_PULSE, 65 | hsync.eq(0) 66 | ).Elif (pixel_counter < H_BACK_PORCH, 67 | hsync.eq(1) 68 | ), 69 | If(pixel_counter >= (H_FRONT_PORCH-1), 70 | # Initilize next line 71 | pixel_counter.eq(0), 72 | line_counter.eq(line_counter + 1), 73 | ), 74 | 75 | # Vertical timing for one screen 76 | If(line_counter < V_SYNC_PULSE, 77 | vsync.eq(0) 78 | ).Else( 79 | vsync.eq(1) 80 | ), 81 | If(line_counter >= (V_FRONT_PORCH-1), 82 | # End of image 83 | line_counter.eq(0) 84 | ), 85 | ] 86 | -------------------------------------------------------------------------------- /gateware/rtl/video/stream_utils.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright (c) 2020 Gregory Davill 2 | # License: BSD 3 | 4 | from migen import * 5 | from litex.soc.interconnect.stream import Endpoint 6 | from migen.genlib.cdc import MultiReg 7 | from rtl.edge_detect import EdgeDetect 8 | 9 | from litex.soc.interconnect.csr import AutoCSR, CSR, CSRStatus, CSRStorage 10 | from litex.soc.interconnect.stream import Endpoint, EndpointDescription, AsyncFIFO 11 | 12 | from litex.soc.interconnect.stream import * 13 | 14 | @ResetInserter() 15 | class StreamPrepend(Module, AutoCSR): 16 | def __init__(self): 17 | self.sink = sink = Endpoint([("data", 32)]) 18 | self.source = source = Endpoint([("data", 32)]) 19 | 20 | self.length = CSRStorage(8) 21 | delay_count = Signal(8) 22 | 23 | 24 | _valid = Signal() 25 | self.sync += _valid.eq(sink.valid) 26 | 27 | self.submodules.fsm = fsm = FSM(reset_state='PASS') 28 | 29 | fsm.act('PASS', 30 | sink.connect(source), 31 | If(~sink.valid, 32 | If(self.length.storage > 0, 33 | NextState('DELAY'), 34 | NextValue(delay_count, self.length.storage) 35 | ) 36 | ) 37 | ) 38 | 39 | fsm.act('DELAY', 40 | sink.connect(source, omit=['first','ready']), 41 | If(delay_count == 0, 42 | NextState('PASS'), 43 | ).Elif(sink.valid & source.ready, 44 | NextValue(delay_count, delay_count - 1), 45 | ), 46 | ) 47 | 48 | 49 | 50 | @ResetInserter() 51 | class StreamAppend(Module, AutoCSR): 52 | def __init__(self): 53 | self.sink = sink = Endpoint([("data", 32)]) 54 | self.source = source = Endpoint([("data", 32)]) 55 | 56 | self.length = CSRStorage(8) 57 | delay_count = Signal(8) 58 | 59 | 60 | _valid = Signal() 61 | self.sync += _valid.eq(sink.valid) 62 | 63 | self.submodules.fsm = fsm = FSM(reset_state='PASS') 64 | 65 | fsm.act('PASS', 66 | sink.connect(source,omit=['last','ready']), 67 | If(sink.last, 68 | If(self.length.storage > 0, 69 | NextState('DELAY'), 70 | NextValue(delay_count, self.length.storage) 71 | ).Else( 72 | source.last.eq(1), 73 | ) 74 | ).Else( 75 | sink.ready.eq(source.ready) 76 | ) 77 | ) 78 | 79 | fsm.act('DELAY', 80 | sink.connect(source, omit=['last','ready']), 81 | If(delay_count == 0, 82 | sink.ready.eq(source.ready), 83 | source.last.eq(1), 84 | NextState('PASS'), 85 | ).Elif(sink.valid & source.ready, 86 | NextValue(delay_count, delay_count - 1), 87 | ), 88 | ) 89 | 90 | 91 | 92 | 93 | ## Unit tests 94 | import unittest 95 | 96 | from litex.soc.interconnect.stream_sim import PacketStreamer, PacketLogger, Packet, Randomizer 97 | from litex.soc.interconnect.stream import Pipeline 98 | 99 | class TestStream(unittest.TestCase): 100 | def testPrepend(self): 101 | data = [i for i in range(2,10)] 102 | length = 5 103 | def generator(dut): 104 | 105 | d = Packet(data) 106 | yield from dut.prepend.length.write(length-1) 107 | yield from dut.streamer.send_blocking(d) 108 | 109 | def checker(dut): 110 | yield from dut.logger.receive() 111 | assert(dut.logger.packet == ([data[0]]*(length) + data)) 112 | 113 | 114 | class DUT(Module): 115 | def __init__(self): 116 | self.submodules.prepend = StreamPrepend() 117 | 118 | self.submodules.streamer = PacketStreamer([("data", 32)]) 119 | self.submodules.logger = PacketLogger([("data", 32)]) 120 | 121 | self.submodules.pipeline = Pipeline( 122 | self.streamer, 123 | self.prepend, 124 | self.logger 125 | ) 126 | 127 | 128 | 129 | dut = DUT() 130 | generators = { 131 | "sys" : [generator(dut), 132 | checker(dut), 133 | dut.streamer.generator(), 134 | dut.logger.generator(), 135 | ] 136 | } 137 | clocks = {"sys": 10} 138 | run_simulation(dut, generators, clocks, vcd_name='test.vcd') 139 | 140 | 141 | def testAppend(self): 142 | data = [i for i in range(32,38)] 143 | length = 3 144 | def generator(dut): 145 | 146 | d = Packet(data) 147 | yield from dut.append.length.write(length-1) 148 | yield from dut.streamer.send_blocking(d) 149 | 150 | def checker(dut): 151 | yield from dut.logger.receive() 152 | assert(dut.logger.packet == (data + [data[-1]]*(length))) 153 | 154 | 155 | class DUT(Module): 156 | def __init__(self): 157 | self.submodules.append = StreamAppend() 158 | 159 | self.submodules.streamer = PacketStreamer([("data", 32)]) 160 | self.submodules.logger = PacketLogger([("data", 32)]) 161 | 162 | self.submodules.pipeline = Pipeline( 163 | self.streamer, 164 | self.append, 165 | self.logger 166 | ) 167 | 168 | 169 | 170 | dut = DUT() 171 | generators = { 172 | "sys" : [generator(dut), 173 | checker(dut), 174 | dut.streamer.generator(), 175 | dut.logger.generator(), 176 | ] 177 | } 178 | clocks = {"sys": 10} 179 | run_simulation(dut, generators, clocks, vcd_name='test.vcd') 180 | 181 | -------------------------------------------------------------------------------- /gateware/rtl/video/terminal.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright (c) 2019 Frank Buss 2 | # License: BSD 3 | 4 | import os 5 | 6 | from migen import * 7 | 8 | from litex.soc.interconnect import wishbone 9 | 10 | from litex.soc.interconnect.stream import Endpoint, EndpointDescription, SyncFIFO, AsyncFIFO 11 | 12 | from litex.soc.interconnect.csr import AutoCSR, CSR, CSRStatus, CSRStorage 13 | 14 | # Terminal emulation with 640 x 480 pixels, 80 x 30 characters, individual foreground and background 15 | # color per character (VGA palette) and user definable font, with code page 437 VGA font initialized. 16 | # 60 Hz framerate, if vga_clk is 25.175 MHz. Independent system clock possible, internal dual-port 17 | # block RAM. 18 | # 19 | # Memory layout: 20 | # 0x0000 - 0x12bf = 2 bytes per character: 21 | # character: index in VGA font 22 | # color: low nibble is foreground color, and high nibble is background color, VGA palette 23 | # 0x12c0 - 0x22bf = VGA font, 16 lines per character, 8 bits width 24 | # 25 | # VGA timings: 26 | # clocks per line: 27 | # 1. HSync low pulse for 96 clocks 28 | # 2. back porch for 48 clocks 29 | # 3. data for 640 clocks 30 | # 4. front porch for 16 clocks 31 | # 32 | # VSync timing per picture (800 clocks = 1 line): 33 | # 1. VSync low pulse for 2 lines 34 | # 2. back porch for 29 lines 35 | # 3. data for 480 lines 36 | # 4. front porch for 10 lines 37 | 38 | # Helpers ------------------------------------------------------------------------------------------ 39 | 40 | def get_path(filename): 41 | """Return filename relative to caller script, if available, otherwise relative to this package 42 | script""" 43 | if os.path.isfile(filename): 44 | return filename 45 | path = os.path.dirname(os.path.realpath(__file__)) 46 | return os.path.join(path, filename) 47 | 48 | def read_ram_init_file(filename, size): 49 | """Read file, if not empty, and test for size. If empty, init with 0""" 50 | if filename == '': 51 | return [0] * size 52 | else: 53 | with open(get_path(filename), "rb") as file: 54 | data = list(file.read()) 55 | if len(data) != size: 56 | raise ValueError("Invalid size for file {}. Expected size: {}, actual size: {}".format( 57 | filename, size, len(data))) 58 | return data 59 | 60 | # Terminal ----------------------------------------------------------------------------------------- 61 | 62 | class Terminal(Module, AutoCSR): 63 | def __init__(self, pads=None, font_filename="util/cp437.bin"): 64 | # Wishbone interface 65 | self.bus = bus = wishbone.Interface(data_width=32) 66 | self.source = source = Endpoint(EndpointDescription([("data", 32)])) 67 | 68 | self.overrun = CSRStatus(32) 69 | 70 | 71 | overrun = Signal(32) 72 | self.comb += self.overrun.status.eq(overrun) 73 | 74 | # Acknowledge immediately 75 | self.sync += [ 76 | bus.ack.eq(0), 77 | If (bus.cyc & bus.stb & ~bus.ack, bus.ack.eq(1)) 78 | ] 79 | 80 | # RAM initialization 81 | font = read_ram_init_file(font_filename, 4096) 82 | ram_init = [0 for i in range(100 * 50 * 2)] + font 83 | 84 | # Create RAM 85 | mem = Memory(width=8, depth=(100 * 50)*2 + 4096, init=ram_init) 86 | self.specials += mem 87 | wrport = mem.get_port(write_capable=True, clock_domain="sys") 88 | self.specials += wrport 89 | rdport = mem.get_port(write_capable=False, clock_domain="vga") 90 | self.specials += rdport 91 | 92 | # Memory map internal block RAM to Wishbone interface 93 | self.sync += [ 94 | wrport.we.eq(0), 95 | If (bus.cyc & bus.stb & bus.we & ~bus.ack, 96 | wrport.we.eq(1), 97 | wrport.dat_w.eq(bus.dat_w), 98 | ), 99 | ] 100 | 101 | self.comb += [ 102 | wrport.adr.eq(bus.adr), 103 | # bus.dat_r.eq(wrport.dat_r) 104 | ] 105 | 106 | # Display resolution 107 | WIDTH = 800 108 | HEIGHT = 600 109 | 110 | # Offset to font data in RAM 111 | FONT_ADDR = 100 * 50 * 2 112 | 113 | # VGA output 114 | self.red = red = Signal(8) if pads is None else pads.red 115 | self.green = green = Signal(8) if pads is None else pads.green 116 | self.blue = blue = Signal(8) if pads is None else pads.blue 117 | self.hsync = hsync = Signal() if pads is None else pads.hsync 118 | self.vsync = vsync = Signal() if pads is None else pads.vsync 119 | 120 | self.blank = blank = Signal() 121 | 122 | 123 | # VGA timings 124 | H_SYNC_PULSE = 128 125 | H_BACK_PORCH = 88 + H_SYNC_PULSE 126 | H_DATA = WIDTH + H_BACK_PORCH 127 | H_FRONT_PORCH = 40 + H_DATA 128 | 129 | V_SYNC_PULSE = 4 130 | V_BACK_PORCH = 23 + V_SYNC_PULSE 131 | V_DATA = HEIGHT + V_BACK_PORCH 132 | V_FRONT_PORCH = 1 + V_DATA 133 | 134 | pixel_counter = Signal(14) 135 | line_counter = Signal(14) 136 | 137 | self.comb += [ 138 | source.ready.eq(0), 139 | If((line_counter >= V_BACK_PORCH) & (line_counter < V_DATA), 140 | If((pixel_counter >= H_BACK_PORCH) & (pixel_counter < (H_DATA)), 141 | source.ready.eq(1), 142 | ) 143 | ), 144 | ] 145 | 146 | self.enable = CSRStorage(1, reset=1) 147 | 148 | # Read address in text RAM 149 | text_addr = Signal(16) 150 | 151 | # Read address in text RAM at line start 152 | text_addr_start = Signal(16) 153 | 154 | # Current line within a character, 0 to 15 155 | fline = Signal(4) 156 | 157 | # Current x position within a character, 0 to 7 158 | fx = Signal(3) 159 | 160 | # Current and next byte for a character line 161 | fbyte = Signal(8) 162 | next_byte = Signal(8) 163 | 164 | # Current foreground color 165 | fgcolor = Signal(24) 166 | next_fgcolor = Signal(24) 167 | 168 | # Current background color 169 | bgcolor = Signal(24) 170 | 171 | # Current fg/bg color index from RAM 172 | color = Signal(8) 173 | 174 | # Color index and lookup 175 | color_index = Signal(4) 176 | color_lookup = Signal(24) 177 | 178 | # VGA palette 179 | palette = [ 180 | 0x000000, 0x0000aa, 0x00aa00, 0x00aaaa, 0xaa0000, 0xaa00aa, 0xaa5500, 0xaaaaaa, 181 | 0x555555, 0x5555ff, 0x55ff55, 0x55ffff, 0xff5555, 0xff55ff, 0xffff55, 0xffffff 182 | ] 183 | cases = {} 184 | for i in range(16): 185 | cases[i] = color_lookup.eq(palette[i]) 186 | self.comb += Case(color_index, cases) 187 | 188 | self.sync.vga += [ 189 | # Default values 190 | red.eq(0), 191 | green.eq(0), 192 | blue.eq(0), 193 | blank.eq(1), 194 | 195 | # Show pixels 196 | If((line_counter >= V_BACK_PORCH) & (line_counter < V_DATA), 197 | If((pixel_counter >= H_BACK_PORCH) & (pixel_counter < (H_DATA)), 198 | blank.eq(0), 199 | If(self.enable.storage, 200 | If(fbyte[7], 201 | red.eq(fgcolor[16:24]), 202 | green.eq(fgcolor[8:16]), 203 | blue.eq(fgcolor[0:8]) 204 | ).Else( 205 | red.eq(bgcolor[16:24]), 206 | green.eq(bgcolor[8:16]), 207 | blue.eq(bgcolor[0:8]) 208 | )).Else( 209 | red.eq(0xaa), 210 | green.eq(0x00), 211 | blue.eq(0xaa) 212 | ), 213 | fbyte.eq(Cat(Signal(), fbyte[:-1])), 214 | ) 215 | ), 216 | 217 | # Load next character code, font line and color 218 | If(fx == 1, 219 | # schedule reading the character code 220 | rdport.adr.eq(text_addr), 221 | text_addr.eq(text_addr + 1) 222 | ), 223 | If(fx == 2, 224 | # Schedule reading the color 225 | rdport.adr.eq(text_addr), 226 | text_addr.eq(text_addr + 1) 227 | ), 228 | If(fx == 3, 229 | # Read character code, and set address for font line 230 | rdport.adr.eq(FONT_ADDR + Cat(Signal(4), rdport.dat_r) + fline) 231 | ), 232 | If(fx == 4, 233 | # Read color 234 | color.eq(rdport.dat_r) 235 | ), 236 | If(fx == 5, 237 | # Read font line, and set color index to get foreground color 238 | next_byte.eq(rdport.dat_r), 239 | color_index.eq(color[0:4]), 240 | ), 241 | If(fx == 6, 242 | # Get next foreground color, and set color index for background color 243 | next_fgcolor.eq(color_lookup), 244 | color_index.eq(color[4:8]) 245 | ), 246 | If(fx == 7, 247 | # Set background color and everything for the next 8 pixels 248 | bgcolor.eq(color_lookup), 249 | fgcolor.eq(next_fgcolor), 250 | fbyte.eq(next_byte) 251 | ), 252 | fx.eq(fx + 1), 253 | If(fx == 7, fx.eq(0)), 254 | 255 | # Horizontal timing for one line 256 | pixel_counter.eq(pixel_counter + 1), 257 | If(pixel_counter < H_SYNC_PULSE, 258 | hsync.eq(0) 259 | ).Elif (pixel_counter < H_BACK_PORCH, 260 | hsync.eq(1) 261 | ), 262 | If(pixel_counter == H_BACK_PORCH - 9, 263 | # Prepare reading first character of next line 264 | fx.eq(0), 265 | text_addr.eq(text_addr_start) 266 | ), 267 | If(pixel_counter == (H_FRONT_PORCH - 1), 268 | # Initilize next line 269 | pixel_counter.eq(0), 270 | line_counter.eq(line_counter + 1), 271 | 272 | # Font height is 16 pixels 273 | fline.eq(fline + 1), 274 | If(fline == 15, 275 | fline.eq(0), 276 | text_addr_start.eq(text_addr_start + 200) 277 | ) 278 | ), 279 | 280 | # Vertical timing for one screen 281 | If(line_counter < V_SYNC_PULSE, 282 | vsync.eq(0) 283 | ).Elif(line_counter < V_BACK_PORCH, 284 | vsync.eq(1) 285 | ), 286 | If(line_counter == (V_FRONT_PORCH - 1), 287 | # End of image 288 | line_counter.eq(0) 289 | ), 290 | If(line_counter == V_BACK_PORCH - 1, 291 | # Prepare generating next image data 292 | fline.eq(0), 293 | text_addr_start.eq(0), 294 | ) 295 | ] 296 | -------------------------------------------------------------------------------- /gateware/rtl/video/video_debug.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright (c) 2020 Gregory Davill 2 | # License: BSD 3 | 4 | import os 5 | 6 | from migen import * 7 | 8 | from litex.soc.interconnect.stream import Endpoint, EndpointDescription, SyncFIFO, AsyncFIFO 9 | from litex.soc.interconnect.csr import AutoCSR, CSR, CSRStatus, CSRStorage 10 | 11 | from litex.soc.cores.freqmeter import FreqMeter 12 | from rtl.edge_detect import EdgeDetect 13 | 14 | 15 | from migen.genlib.cdc import MultiReg, PulseSynchronizer 16 | 17 | class VideoDebug(Module, AutoCSR): 18 | def __init__(self, clk_freq): 19 | 20 | # VGA input 21 | self.red = red = Signal(8) 22 | self.green = green = Signal(8) 23 | self.blue = blue = Signal(8) 24 | self.hsync = hsync = Signal() 25 | self.vsync = vsync = Signal() 26 | self.data_valid = data_valid = Signal() 27 | 28 | 29 | self.submodules.freq = FreqMeter(period=clk_freq, clk=ClockSignal("pixel")) 30 | 31 | self.submodules.v_edge = v_edge = EdgeDetect(mode="change", input_cd="pixel", output_cd="pixel") 32 | self.submodules.h_edge = h_edge = EdgeDetect(mode="change", input_cd="pixel", output_cd="pixel") 33 | 34 | vsync_low = Signal(24) 35 | vsync_high = Signal(24) 36 | vsync_counter = Signal(24) 37 | 38 | hsync_low = Signal(24) 39 | hsync_high = Signal(24) 40 | hsync_counter = Signal(24) 41 | 42 | lines = Signal(16) 43 | lines_counter = Signal(16) 44 | 45 | self.comb += v_edge.i.eq(vsync) 46 | self.comb += h_edge.i.eq(hsync) 47 | self.sync.pixel += [ 48 | vsync_counter.eq(vsync_counter + 1), 49 | hsync_counter.eq(hsync_counter + 1), 50 | If(h_edge.o, 51 | If(hsync, 52 | hsync_low.eq(hsync_counter) 53 | ).Else( 54 | hsync_high.eq(hsync_counter), 55 | lines_counter.eq(lines_counter + 1) 56 | ), 57 | hsync_counter.eq(1) 58 | ), 59 | If(v_edge.o, 60 | If(vsync, 61 | vsync_low.eq(vsync_counter) 62 | ).Else( 63 | vsync_high.eq(vsync_counter), 64 | lines.eq(lines_counter), 65 | lines_counter.eq(1), 66 | ), 67 | vsync_counter.eq(1) 68 | ), 69 | 70 | ] 71 | 72 | # CSRs 73 | self.latch = CSR() 74 | self.vsync_low = CSRStatus(24) 75 | self.vsync_high = CSRStatus(24) 76 | self.hsync_low = CSRStatus(24) 77 | self.hsync_high = CSRStatus(24) 78 | self.lines = CSRStatus(16) 79 | 80 | # output values to CSR 81 | 82 | latch = Signal() 83 | latch_ps = PulseSynchronizer("sys", "pixel") 84 | self.submodules += latch_ps 85 | self.comb += latch_ps.i.eq(self.latch.re) 86 | 87 | _vsync_low = Signal(24) 88 | _vsync_high = Signal(24) 89 | _hsync_low = Signal(24) 90 | _hsync_high = Signal(24) 91 | _lines = Signal(16) 92 | 93 | self.sync.pixel += [ 94 | If(latch_ps.o, 95 | _vsync_low.eq(vsync_low), 96 | _vsync_high.eq(vsync_high), 97 | _hsync_low.eq(hsync_low), 98 | _hsync_high.eq(hsync_high), 99 | _lines.eq(lines), 100 | ) 101 | ] 102 | self.specials += MultiReg(_vsync_low, self.vsync_low.status) 103 | self.specials += MultiReg(_vsync_high, self.vsync_high.status) 104 | self.specials += MultiReg(_hsync_low, self.hsync_low.status) 105 | self.specials += MultiReg(_hsync_high, self.hsync_high.status) 106 | self.specials += MultiReg(_lines, self.lines.status) 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /gateware/rtl/video/video_stream.py: -------------------------------------------------------------------------------- 1 | # This file is Copyright (c) 2020 Gregory Davill 2 | # License: BSD 3 | 4 | from migen import * 5 | from litex.soc.interconnect.stream import Endpoint 6 | 7 | class VideoStream(Module): 8 | def __init__(self): 9 | # VGA output 10 | self.red = red = Signal(8) 11 | self.green = green = Signal(8) 12 | self.blue = blue = Signal(8) 13 | self.data_valid = data_valid = Signal() 14 | 15 | self.source = source = Endpoint([("data", 32)]) 16 | 17 | self.sync.pixel += [ 18 | source.valid.eq(data_valid), 19 | source.data.eq(Cat(red, green, blue)), 20 | ] 21 | -------------------------------------------------------------------------------- /gateware/util/colours.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/gateware/util/colours.png -------------------------------------------------------------------------------- /gateware/util/cp437.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/gateware/util/cp437.bin -------------------------------------------------------------------------------- /software/fw-updater/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /software/fw-updater/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace fw_updater 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// The main entry point for the application. 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new Form1()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /software/fw-updater/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("fw-updater")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("fw-updater")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("91ee0d69-a032-4676-9e5a-567c814d0ba2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /software/fw-updater/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace fw_updater.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("fw_updater.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap _0173_hdd_down { 67 | get { 68 | object obj = ResourceManager.GetObject("_0173_hdd_down", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Drawing.Bitmap. 75 | /// 76 | internal static System.Drawing.Bitmap _0853_notification { 77 | get { 78 | object obj = ResourceManager.GetObject("_0853_notification", resourceCulture); 79 | return ((System.Drawing.Bitmap)(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// Looks up a localized resource of type System.Drawing.Bitmap. 85 | /// 86 | internal static System.Drawing.Bitmap _0858_checkmark_circle { 87 | get { 88 | object obj = ResourceManager.GetObject("_0858_checkmark_circle", resourceCulture); 89 | return ((System.Drawing.Bitmap)(obj)); 90 | } 91 | } 92 | 93 | /// 94 | /// Looks up a localized resource of type System.Drawing.Bitmap. 95 | /// 96 | internal static System.Drawing.Bitmap _0859_cross_circle { 97 | get { 98 | object obj = ResourceManager.GetObject("_0859_cross_circle", resourceCulture); 99 | return ((System.Drawing.Bitmap)(obj)); 100 | } 101 | } 102 | 103 | /// 104 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). 105 | /// 106 | internal static System.Drawing.Icon fw_update { 107 | get { 108 | object obj = ResourceManager.GetObject("fw_update", resourceCulture); 109 | return ((System.Drawing.Icon)(obj)); 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /software/fw-updater/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\fw-update.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | 125 | ..\Resources\0173-hdd-down.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 126 | 127 | 128 | ..\Resources\0853-notification.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | 131 | ..\Resources\0858-checkmark-circle.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 132 | 133 | 134 | ..\Resources\0859-cross-circle.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 135 | 136 | -------------------------------------------------------------------------------- /software/fw-updater/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace fw_updater.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /software/fw-updater/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /software/fw-updater/Resources/0173-hdd-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/software/fw-updater/Resources/0173-hdd-down.png -------------------------------------------------------------------------------- /software/fw-updater/Resources/0853-notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/software/fw-updater/Resources/0853-notification.png -------------------------------------------------------------------------------- /software/fw-updater/Resources/0858-checkmark-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/software/fw-updater/Resources/0858-checkmark-circle.png -------------------------------------------------------------------------------- /software/fw-updater/Resources/0859-cross-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/software/fw-updater/Resources/0859-cross-circle.png -------------------------------------------------------------------------------- /software/fw-updater/Resources/dfu_util.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/software/fw-updater/Resources/dfu_util.exe -------------------------------------------------------------------------------- /software/fw-updater/Resources/fw-update.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregdavill/DiVA-firmware/fb927dcca4b5751083cb5eaf8735e6c31a27992e/software/fw-updater/Resources/fw-update.ico -------------------------------------------------------------------------------- /software/fw-updater/fw-updater.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {91EE0D69-A032-4676-9E5A-567C814D0BA2} 8 | WinExe 9 | fw_updater 10 | fw-updater 11 | v4.5 12 | 512 13 | true 14 | publish\ 15 | true 16 | Disk 17 | false 18 | Foreground 19 | 7 20 | Days 21 | false 22 | false 23 | true 24 | 0 25 | 1.0.0.%2a 26 | false 27 | false 28 | true 29 | 30 | 31 | AnyCPU 32 | true 33 | full 34 | false 35 | bin\Debug\ 36 | DEBUG;TRACE 37 | prompt 38 | 4 39 | 40 | 41 | AnyCPU 42 | pdbonly 43 | true 44 | bin\Release\ 45 | TRACE 46 | prompt 47 | 4 48 | 49 | 50 | Resources\fw-update.ico 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Form 68 | 69 | 70 | Form1.cs 71 | 72 | 73 | 74 | 75 | Form1.cs 76 | 77 | 78 | ResXFileCodeGenerator 79 | Resources.Designer.cs 80 | Designer 81 | 82 | 83 | True 84 | Resources.resx 85 | True 86 | 87 | 88 | 89 | SettingsSingleFileGenerator 90 | Settings.Designer.cs 91 | 92 | 93 | True 94 | Settings.settings 95 | True 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | False 105 | .NET Framework 3.5 SP1 106 | false 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /software/fw-updater/fw-updater.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.168 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "fw-updater", "fw-updater.csproj", "{91EE0D69-A032-4676-9E5A-567C814D0BA2}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {91EE0D69-A032-4676-9E5A-567C814D0BA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {91EE0D69-A032-4676-9E5A-567C814D0BA2}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {91EE0D69-A032-4676-9E5A-567C814D0BA2}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {91EE0D69-A032-4676-9E5A-567C814D0BA2}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {29F3765F-BDE2-4C5C-A53B-D4B3A3AED549} 24 | EndGlobalSection 25 | EndGlobal 26 | --------------------------------------------------------------------------------