diff --git a/.gitignore b/.gitignore index 4f9a394..ce61271 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +/packages **/.vscode diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..55350ea --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "sw/cic"] + path = sw/cic + url = https://github.com/ManCloud/UltraCIC-III.git + ignore = dirty diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9ec6487 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +os: linux +dist: focal +arch: amd64 + +language: shell + +services: + - docker + +before_install: + - sudo apt-get update + - sudo apt-get install -y avra + +script: + ./build_release.sh "${TRAVIS_BRANCH}-${TRAVIS_TAG}" + +deploy: + provider: releases + token: + secure: LNBOWTFd5wz3sR7ec4OA/wbVjIWFCv2WJnx6QBIOhgh2VIicNd91ZgMxq77T7/DBxPJ7GhAuG76s4K68ve8m5+klhpjtxVwstDzHBaNbRIUbhxjKh8oJGX8T+qeB60cnjz1YwmGASWSXNpXPhW/SZRw1lAjpv3W2cxTmDyAzayV+xO7HeVfOAgaKOAL600LwjEdWGLFVRx2MQqMe2NAJ4XWu8oJBXjrlQc/5jHW/3o4oFILs+Jr7VwRt3qYrU0xJ8XPJFDo7AWvAHYH4vrACIOXMPIuI29tsi9eWcT/+q96IKmpDPRvn3nrMnb7bMN995WyW2tFvGqABsFRwUXJ6Ydc7aFdTAC2+u/cWpR4AEpLGNWFqpDlLwjNfqiZ1rhDDAlEI/bA+zU/qyziT5lDaARFJtRxuQIWeZRa7EgmaTc0izghrzIBC807+G1cUTGntSjgVb8B6rtJa6tBt3/9Iu4RmRxJnKkgWQBNo8CEVVDAe/Ms7igashEoaidr545eR/9ervxGLftFUctKWjnsEL4r7ifjUOj9h/E7BZx61WkJ8hORoMValcc+Kv0aCmkzWKe0UsSsuMzsXHc3x782AO2Xq5fZLk0urdqQ/uzu5crSnReJh/lCZxjg11BG31r5kAixBaSkiJFFrAs+GOSR/IskRn8gKThjZtna8DUnHApE= + file: packages/SummerCart64-${TRAVIS_BRANCH}-${TRAVIS_TAG}.zip + on: + repo: Polprzewodnikowy/SummerCollection + tags: true + draft: true diff --git a/README.md b/README.md index 8e00d67..bc63fbd 100644 --- a/README.md +++ b/README.md @@ -2,42 +2,69 @@ A collection of hardware, firmware and software designs of SummerCart64 - Nintendo 64 FlashCart/DevKit. + ## Project parts -### Hardware - -Folder **`hw`** contains PCB design made in Autodesk Eagle. - ### Firmware Folder **`fw`** contains firmware written in Verilog for Intel MAX10 FPGA. +### Hardware + +Folder **`hw`** contains PCB design made in Autodesk Eagle. + ### Software -Folder **`sw`** contains several helper programs that makes flash cart work all together, including bootloader and PC communication software. +Folder **`sw`** contains several helper programs that makes flash cart work all together, including bootloader and CIC emulation. + ## What works Currently hardware implements basic functionality for playing games - ROM emulation, 4/16K EEPROM, bootloader and PC communication. You can send any ROM to the SDRAM from PC and if game doesn't check for save hardware other than EEPROM then it most likely will work. Bootloader does all the work necessary to setup the console registers for specific CIC chip that game requires. + +## How to build + +Instructions below are mostly written for Windows users and point to Windows specific executables. + +1. Download latest zip files from [releases tab](https://github.com/Polprzewodnikowy/SummerCollection/releases). +2. Order PCBs in your farovite company, all gerbers, bill of materials and schematics are in **`SummerCart64_PCB-main-[version].zip`** file. +3. Order all necessary components, eg. in [Mouser](https://mouser.com/). +4. Solder all components onto the PCB. +5. Connect flashcart to PC. +6. Download [FT_PROG](https://www.ftdichip.com/Support/Utilities.htm#FT_PROG) from FTDI website. +7. Open FT_PROG and scan for devices. +8. Load `ftdi-template.xml` from **`SummerCart64-main-[version].zip`** located in `hw` folder. +9. Program FTDI chip, then right click on the device and select "Cycle port" +10. Download and install [Arrow USB Blaster drivers](https://shop.trenz-electronic.de/en/Download/?path=Trenz_Electronic/Software/Drivers/Arrow_USB_Programmer/Arrow_USB_Programmer_2.4) from Trenz Electronic website. +11. Download and install [QuartusProgrammer](https://download.altera.com/akdlm/software/acdsinst/20.1std/711/ib_installers/QuartusProgrammerSetup-20.1.0.711-windows.exe) from Intel website. +12. Click on "Hardware Setup..." button and select "Arrow-USB-Blaster [AR*xxxxxx*]" in "Currently selected hardware", then close window. +13. Click on "Auto Detect" button and select "10M08SC" option. +14. Click on the newly shown row, then click on "Change File..." button and select `SummerCart64.pof` file from **`SummerCart64-main-[version].zip`** located in `fw/output_files` folder. +15. Select "Program/Configure" and "Verify" checkboxes. +16. Click on "Start" button and wait for operation to finish. +17. Download latest release of [N64-UNFLoader](https://github.com/buu342/N64-UNFLoader). +18. Insert flashcart in the Nintendo 64. +19. Upload ROM by executing `UNFLoader -r your_rom_file.n64` +20. Turn on console and enjoy playing homebrew games on real hardware. + + ## Issues -There are several issues with the project at the moment in order of importance: +There are several issues with the project at the moment in order of importance. Keep in mind that core flashcart functionality is fully working: -- Documentation is not finished for most of the modules. -- PCB schematic is unorganized and some component values are missing. -- There's no BOM for hardware. -- Currently PC communication disables N64 PI interface completely as there's no bus arbiter implemented. +- No debug interface for the [N64-UNFLoader](https://github.com/buu342/N64-UNFLoader). +- No read functionality through USB interface. +- No SRAM/FlashRAM save hardware implementation. - No SD card interface hardware implementation. - No save write-back to SD card hardware implementation. -- PCB design and necessary components needs to be reconsidered for next version. -- No SRAM/FlashRAM save hardware implementation. - No RTC hardware implementation. -- No CIC implementation in FPGA, current solution uses [UltraCIC II](https://github.com/perkinsb1024/UltraCIC-II) based on ATtiny45. + ## What's next -Current goal is to document the project. +Current goal is to write debug module for the [N64-UNFLoader](https://github.com/buu342/N64-UNFLoader). + ## Finished sample diff --git a/build_pcb.sh b/build_pcb.sh new file mode 100755 index 0000000..8c71591 --- /dev/null +++ b/build_pcb.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +PACKAGES_FOLDER_NAME="packages" +PACKAGE_FILE_NAME="SummerCart64_PCB" +FILES=( + "./hw/CAMOutputs" + # Manually created files + "./hw/SummerCart64_sch.pdf" + "./hw/SummerCart64_brd_top.pdf" + "./hw/SummerCart64_brd_bot.pdf" + "./hw/SummerCart64_brd_place_top.pdf" +) + + +# Add version to zip file name if provided +if [[ $1 ]]; then + PACKAGE_FILE_NAME="${PACKAGE_FILE_NAME}-${1}" +fi + + +# Generate Gerbers +pushd hw +if [[ -e CAMOutputs ]]; then + rm -rf CAMOutputs +fi +echo "Generating Gerbers" +eaglecon.exe -X -dCAMJOB -jSummerCart64.cam SummerCart64.brd +popd + + +# Create packages directory +echo "Creating ${PACKAGES_FOLDER_NAME} directory" +mkdir -p "${PACKAGES_FOLDER_NAME}" + + +# ZIP files for release +echo "Zipping PCB files" +if [[ -e "${PACKAGES_FOLDER_NAME}/${PACKAGE_FILE_NAME}.zip" ]]; then + rm -f "${PACKAGES_FOLDER_NAME}/${PACKAGE_FILE_NAME}.zip" +fi +zip -r "${PACKAGES_FOLDER_NAME}/${PACKAGE_FILE_NAME}.zip" ${FILES[@]} diff --git a/build_release.sh b/build_release.sh new file mode 100755 index 0000000..1b40417 --- /dev/null +++ b/build_release.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +PACKAGES_FOLDER_NAME="packages" +PACKAGE_FILE_NAME="SummerCart64" +FILES=( + "./fw/output_files/SummerCart64.pof" + "./hw/ftdi-template.xml" + "./sw/cic/UltraCIC-III.hex" + "./sw/cic/UltraCIC-III.eep.hex" +) + + +# Add version to zip file name if provided +if [[ $1 ]]; then + PACKAGE_FILE_NAME="${PACKAGE_FILE_NAME}-${1}" +fi + + +# Build bootloader +echo "Building bootloader" +pushd sw/bootloader +docker run -t --mount type=bind,src="$(pwd)",target="/libdragon" anacierdem/libdragon:4.1.1 /bin/bash -c "/usr/bin/make clean; /usr/bin/make all N64_BYTE_SWAP=false" +popd + + +# Build UltraCIC-III +pushd sw/cic +echo "Building UltraCIC-III" +avra UltraCIC-III.asm -D attiny45 +popd + + +# Build FPGA firmware +echo "Building FPGA firmware" +docker run -t --mount type=bind,src="$(pwd)",target="/build" chriz2600/quartus-lite:20.1.0 /usr/local/bin/quartus_wrapper quartus_sh --flow compile /build/fw/SummerCart64.qpf + + +# Create packages directory +echo "Creating ${PACKAGES_FOLDER_NAME} directory" +mkdir -p "${PACKAGES_FOLDER_NAME}" + + +# ZIP files for release +echo "Zipping files" +if [[ -e "${PACKAGES_FOLDER_NAME}/${PACKAGE_FILE_NAME}.zip" ]]; then + rm -f "${PACKAGES_FOLDER_NAME}/${PACKAGE_FILE_NAME}.zip" +fi +zip -r "${PACKAGES_FOLDER_NAME}/${PACKAGE_FILE_NAME}.zip" ${FILES[@]} diff --git a/fw/.gitignore b/fw/.gitignore index ac1e7c7..34a27b1 100644 --- a/fw/.gitignore +++ b/fw/.gitignore @@ -2,6 +2,8 @@ /greybox_tmp /incremental_db /output_files +**/.qsys_edit *.qws *.rpt *.txt +*.sopcinfo diff --git a/fw/README.md b/fw/README.md index fe6b415..1b5b74f 100644 --- a/fw/README.md +++ b/fw/README.md @@ -2,18 +2,16 @@ A FPGA firmware written in Verilog for SummerCart64. + # Technical Reference Manual + ## Bus peripheral addresses - **`0x1000 0000 - 0x13FF FFFF`** - [R/W] *SDRAM Memory* -- **`0x1000 0000 - 0x10FF FFFF`** - [R] *Flash Memory* (no remap) -- **`0x1800 0000 - 0x18FF FFFF`** - [R] *Flash Memory* (remapped) -- **`0x1C00 0000 - 0x1C00 0000`** - [R/W] *Flash Registers* - **`0x1D00 0000 - 0x1D00 07FF`** - [R/W] *EEPROM Memory* -- **`0x1D10 0000 - 0x1D10 03FF`** - [R] *Debug RX FIFO* (unimplemented) -- **`0x1D10 0800 - 0x1D10 0BFF`** - [W] *Debug TX FIFO* (unimplemented) -- **`0x1E00 0000 - 0x1E00 0014`** - [R/W] *Cart Registers* +- **`0x1E00 0000 - 0x1E00 0007`** - [R/W] *Cart Registers* + ## Memory spaces @@ -25,14 +23,6 @@ Access: Read or Write, 2 byte (16 bit) aligned 64 MB of SDRAM memory. Available on the bus when **SDRAM** bit in **CART->CR** register is set. Used as ROM storage. -### Flash Memory - -Base address: **`0x1000 0000`** (no remap) or **`0x1800 0000`** (remapped)\ -Length: **`16 MB`**\ -Access: Read only, 2 byte (16 bit) aligned - -16 MB of Flash Memory. Available on the bus when **FLASH** bit in **CART->CR** register is set. Used as bootloader storage. - ### EEPROM Memory Base address: **`0x1D00 0000`**\ @@ -41,23 +31,6 @@ Access: Read or Write, 4 byte (32 bit) aligned 2 kB of EEPROM Memory. Available on the bus when **EEPROM_PI** bit in **CART->CR** register is set. Used to upload/download EEPROM contents to/from PC. -### Debug FIFO DMA (unimplemented) - -#### RX FIFO DMA Memory (unimplemented) - -Base address: **`0x1D100 0000`**\ -Length: **`1kB`**\ -Access: Read only, 4 byte (32 bit) aligned - -1 kB of RX FIFO. Available on the bus when **DEBUG** bit in **CART->CR** register is set. Used to receive arbitrary data from PC. Only 4 byte reads are supported, for single byte reads use **CART->DEBUG_RX** register. - -#### TX FIFO DMA Memory (unimplemented) - -Base address: **`0x1D100 0800`**\ -Length: **`1kB`**\ -Access: Write only, 4 byte (32 bit) aligned - -1 kB of TX FIFO. Available on the bus when **DEBUG** bit in **CART->CR** register is set. Used to send arbitrary data to PC. Only 4 byte writes are supported, for single byte writes use **CART->DEBUG_TX** register. ## Registers @@ -68,34 +41,29 @@ Base address: **`0x1E00 0000`** #### Configuration register (**CR**) Address offset: **`0x00`**\ -Powerup value: **`0b0000 0001`**\ -Soft reset value: **`0b00xx x001`**\ +Powerup value: **`0b0000 0000`**\ +Soft reset value: **`0b00xx x000`**\ Access: Read or write, 4 byte (32 bit) aligned This register is used to enable or disable various modules available on the cart. - 31:6 | 5 | 4 | 3 | 2 | 1 | 0 ---------|-------|------------|-----------|-----------|-------|------- - 0 | DEBUG | EEPROM_16K | EEPROM_SI | EEPROM_PI | SDRAM | FLASH - x | R/W | R/W | R/W | R/W | R/W | R/W + 31:5 | 4 | 3 | 2 | 1 | 0 +------|------------|-----------|---|----------|--- + 0 | EEPROM_16K | EEPROM_EN | 0 | SDRAM_EN | 0 + x | R/W | R/W | x | R/W | x -- Bits 31:6: Reserved. Reads as 0, writes are ignored but it's recommended to be set as 0 for future compatibility. -- Bit 5 **DEBUG** (unimplemented): Enable debug FIFO access at address base **`0x1D100 0000`** and **`0x1D100 0800`**. - - 0: Debug FIFO disabled - - 1: Debug FIFO enabled +- Bits 31:5: Reserved. Reads as 0, writes are ignored but it's recommended to be set as 0 for future compatibility. - Bit 4 **EEPROM_16K**: Sets ID returned by EEPROM to identify itself as 4k or 16k variant. - 0: EEPROM 4k variant - 1: EEPROM 16k variant -- Bit 3 **EEPROM_SI**: Enable EEPROM access through SI bus. +- Bit 3 **EEPROM_EN**: Enable EEPROM access through SI bus. - 0: EEPROM SI access disabled - 1: EEPROM SI access enabled -- Bit 2 **EEPROM_PI**: Enable EEPROM access through PI bus at address base **`0x1D00 0000`**. Cleared by hardware on N64 Reset/NMI event. - - 0: EEPROM PI access disabled - - 1: EEPROM PI access enabled -- Bit 1 **SDRAM**: Enable SDRAM access at address base **`0x1000 0000`**. This bit also remaps Flash base address. Cleared by hardware on N64 Reset/NMI event. +- Bit 2: Reserved. Reads as 0, writes are ignored but it's recommended to be set as 0 for future compatibility. +- Bit 1 **SDRAM_EN**: Enable SDRAM access at address base **`0x1000 0000`**. When disabled bootloader flash image is mapped at this address. Cleared by hardware on N64 Reset/NMI event. - 0: SDRAM disabled - 1: SDRAM enabled -- Bit 0 **FLASH**: Enable Flash access at address base **`0x1000 0000`** (**SDRAM** = 0) or **`0x1800 0000`** (**SDRAM** = 1). Set by hardware on N64 Reset/NMI event. +- Bit 0: Reserved. Reads as 0, writes are ignored but it's recommended to be set as 0 for future compatibility. - 0: Flash disabled - 1: Flash enabled @@ -107,13 +75,12 @@ Access: Read or write, 4 byte (32 bit) aligned This register is used for PC -> bootloader communication. - 31:8 | 7:6 | 5:4 | 3:0 ---------|--------|---------|---------- - 0 | SWITCH | TV_TYPE | CIC_TYPE - x | R/W | R/W | R/W + 31:6 | 5:4 | 3:0 +--------|---------|---------- + SWITCH | TV_TYPE | CIC_TYPE + R/W | R/W | R/W -- Bits 31:8: Reserved. Reads as 0, writes are ignored but it's recommended to be set as 0 for future compatibility. -- Bits 7:6 **SWITCH**: Additional bits that can be passed to bootloader, currently unused. +- Bits 31:6 **SWITCH**: Additional bits that can be passed to bootloader, currently unused. - Bits 5:4 **TV_TYPE**: Overrides TV type in bootloader. Used only when **CIC_TYPE** value is valid (values 1 - 7). - 0: PAL TV type - 1: NTSC TV type @@ -129,469 +96,3 @@ This register is used for PC -> bootloader communication. - 6: CIC X106 - 7: CIC 8303 - 8 - 15: Same effect as value 0 - -#### Debug single byte RX FIFO access (**DEBUG_RX**) (unimplemented) - -Address offset: **`0x08`** and **`0x0C`**\ -Access: Read only, 4 byte (32 bit) aligned - -This register grabs single byte from debug RX FIFO. - -Due to how PI interface read prefetch is implemented this register exists on 2 addresses but it's necessary to do only single read on address offset **`0x08`**. Never read from address **`0x0C`** directly as it will not perform read action properly (read pointer is not incremented). - - 31:8 | 7:0 --------|------------ - 0 | RX_DATA - x | R - -- Bits 31:8: Reserved. Reads as 0. -- Bits 7:0 **RX_DATA**: Data read from debug RX FIFO. - -#### Debug single byte TX FIFO access (**DEBUG_TX**) (unimplemented) - -Address offset: **`0x10`**\ -Access: Write only, 4 byte (32 bit) aligned - -This register puts single byte on debug TX FIFO. - - 31:8 | 7:0 --------|------------ - 0 | TX_DATA - x | W - -- Bits 31:8: Reserved. Writes are ignored but it's recommended to be set as 0 for future compatibility. -- Bits 7:0 **TX_DATA**: Data to be written to debug TX FIFO. - -#### Debug status register (**DEBUG_SR**) (unimplemented) - -Address offset: **`0x14`**\ -Access: Read or write, 4 byte (32 bit) aligned - -This register is used for reading status and flushing debug RX/TX FIFOs. - - 31:24 | 23 | 22 | 21:11 | 10:0 ---------|----------|----------|------------|------------ - 0 | TX_FLUSH | RX_FLUSH | TX_FIFO_UB | RX_FIFO_UB - x | W | W | R | R - -- Bits 31:24: Reserved. Reads as 0, writes are ignored but it's recommended to be set as 0 for future compatibility. -- Bit 23 **TX_FLUSH**: Flushes TX FIFO. - - 0: No action - - 1: Flush TX FIFO -- Bit 22 **RX_FLUSH**: Flushes RX FIFO. - - 0: No action - - 1: Flush RX FIFO -- Bits 21:11 **TX_FIFO_UB**: Number of bytes waiting to be read by PC. -- Bits 10:0 **RX_FIFO_UB**: Number of bytes waiting to be processed by N64. - -### Flash (**FLASH**) registers - -Base address: **`0x1C00 0000`** - -#### Arbitrary command register (**ACR**) - -Address offset: **`0x00`**\ -Access: Read or write, 4 byte (32 bit) aligned - -This register is used to send arbitrary commands to Flash chip, useful for erasing and programming memory from PC or N64. Extended register documentation is available at [this webpage](https://zipcpu.com/blog/2019/03/27/qflexpress.html). - - 31:13 | 12 | 11 | 10 | 9 | 8 | 7:0 ----------|------|------|----|-----------|-------------|------- - 0 | MODE | QUAD | 0 | DIRECTION | CHIP_SELECT | DATA - x | W | W | x | W | W | R/W - -- Bits 31:13: Reserved. Reads as 0, writes are ignored but it's recommended to be set as 0 for future compatibility -- Bit 12 **MODE**: Sets flash module arbitrary command mode. - - 0: Normal mode, no command is send to the Flash chip - - 1: Arbitrary command mode. When set no reads should be made on the *Flash Memory* address space -- Bit 11 **QUAD**: Sets next Q/SPI transaction transfer speed. - - 0: SPI 1-bit transaction - - 1: QSPI 4-bit transaction -- Bit 10: Reserved. Reads as 0, writes are ignored but it's recommended to be set as 0 for future compatibility -- Bit 9 **DIRECTION**: Sets next QSPI transaction direction. This bit has no effect when **QUAD** bit is set to 0 - - 0: QSPI read transaction - - 1: QSPI write transaction -- Bit 8 **CHIP_SELECT**: Sets next Q/SPI transaction CS pin value and enables/disables next transaction. - - 0: Deselects Flash chip, no transaction is made - - 1: Selects Flash chip, transaction is made -- Bits 7:0 **DATA**: Data transferred to/from Flash chip - - Read: Data returned from last transaction - - Write: Data to be written in next transaction - -## PC communication - -PC <-> Cart communication uses SPI as hardware layer. Communication is command based and PC is always responsible for transfer initiation. - -Module contains two 1024 word (4 kB) FIFOs used as a gate between SPI clock domain and Cart clock domain. Debug communication uses its own set of FIFOs, 1 kB in size each. - -### Commands - -Every command starts with setting CS pin low, then sending command, writing and/or reading data and setting CS pin high. Only SPI mode 0 is supported. - -#### Status (**0x00**) - -Reads PC communication module status word. - -Command bytes: - - Byte(s) | 0 | 1:4 ------------|------|------------- - Value | 0x00 | Status word - Direction | W | R - -Status word bits: - - 31:24 | 23 | 22 | 21:11 | 10:0 --------|-----------|-------------|------------|------------ - ID | ADDR_INCR | N64_DISABLE | TX_FIFO_UW | RX_FIFO_UW - -- Bits 31:24 **ID**: Always **`0xAA`**, can be used to identify proper communication with cart. -- Bit 23 **ADDR_INCR**: Returns current status of **ADDR_INCR** bit in configuration. - - 0: No address increment - - 1: Address increment -- Bit 22 **N64_DISABLE**: Returns current status of **N64_DISABLE** bit in configuration. - - 0: N64 PI interface enabled, PC bus access disabled - - 1: N64 PI interface disabled, PC bus access enabled -- Bits 21:11 **TX_FIFO_UW**: Number of 4 byte (32 bit) words waiting to be processed by bus controller. -- Bits 10:0 **RX_FIFO_UW**: Number of 4 byte (32 bit) words waiting to be read by PC. - -Example - read status word: - - Byte | 0 | 1 | 2 | 3 | 4 -------|------|------|------|------|------ - TX | 0x00 | x | x | x | x - RX | x | 0xAA | 0x00 | 0x00 | 0x00 - -*x = don't care* - -#### Config (**0x10**) - -Sets PC communication module bus controller configuration. - -This command consumes space in TX FIFO. Check TX FIFO availability before issuing this command. - -Command bytes: - - Byte(s) | 0 | 1:4 ------------|------|-------------------- - Value | 0x10 | Configuration word - Direction | W | W - -Configuration word bits: - - 31:2 | 1 | 0 -------|-----------|------------- - x | ADDR_INCR | N64_DISABLE - -- Bits 31:2: Reserved. Writes are ignored but it's recommended to be set as 0 for future compatibility. -- Bit 1 **ADDR_INCR**: Sets address increment mode when reading or writing to bus. Useful for writing/reading many values to/from single address. - - 0: No address increment - - 1: Address increment -- Bit 0 **N64_DISABLE**: Disables N64 PI interface and enables communication module access to the bus. - - 0: N64 PI interface enabled, PC bus access disabled - - 1: N64 PI interface disabled, PC bus access enabled - -Example - set address increment and disable N64: - - Byte | 0 | 1 | 2 | 3 | 4 -------|------|------|------|------|------ - TX | 0x10 | 0x00 | 0x00 | 0x00 | 0x03 - RX | x | x | x | x | x - -*x = don't care* - -#### Set address (**0x20**) - -Sets starting bus address. - -This command consumes space in TX FIFO. Check TX FIFO availability before issuing this command. - -Command bytes: - - Byte(s) | 0 | 1:4 ------------|------|-------------- - Value | 0x20 | Address word - Direction | W | W - -Address word bits: - - 31:0 | ----------| - ADDRESS | - -- Bits 31:0 **ADDRESS**: Starting address in bus address space. - -Example - set starting address **`0x1034 5678`**: - - Byte | 0 | 1 | 2 | 3 | 4 -------|------|------|------|------|------ - TX | 0x20 | 0x10 | 0x34 | 0x56 | 0x78 - RX | x | x | x | x | x - -*x = don't care* - -#### Initiate read to RX FIFO (**0x30**) - -Initiates read of X 4 byte (32 bit) words to RX FIFO. Maximum possible read length is 64 MB. However, it's possible to send multiple commands in series to achieve longer read lengths. After issuing this command it's necesarry to read data with "Read from RX FIFO (0x50)" command. Command will read dummy data when **N64_DISABLE** bit is cleared in communication module configuration. - -This command consumes space in TX FIFO. Check TX FIFO availability before issuing this command. - -Command bytes: - - Byte(s) | 0 | 1:4 ------------|------|------------------ - Value | 0x30 | Read length word - Direction | W | W - -Read length word bits: - - 31:24 | 23:0 | --------|-------------| - x | READ_LENGTH | - -- Bits 31:24: Reserved. Writes are ignored but it's recommended to be set as 0 for future compatibility. -- Bits 23:0 **READ_LENGTH**: Number of words to be read to RX FIFO minus one. - -Example - fill RX FIFO with 5 words (20 bytes): - - Byte | 0 | 1 | 2 | 3 | 4 -------|------|------|------|------|------ - TX | 0x30 | 0x00 | 0x00 | 0x00 | 0x04 - RX | x | x | x | x | x - -*x = don't care* - -#### Write to TX FIFO (**0x40**) - -Writes data to TX FIFO. Sent words then are processed immediately as bus writes at current internal bus address. Data can be written only as 4 byte (32 bit) words. When **N64_DISABLE** bit in communication module configuration is cleared then command will process data in TX FIFO but it won't write anything to bus. - -This command consumes space in TX FIFO. Check TX FIFO availability before issuing this command. - -Command bytes: - - Byte(s) | 0 | 1:4 | 5:8 | 9:12 | ... ------------|------|-----------|-----------|-----------|----- - Value | 0x40 | Data word | Data word | Data word | ... - Direction | W | W | W | W | ... - -Data word bits: - - 31:0 | -------| - DATA | - -- Bits 31:0 **DATA**: Word to be written to bus. - -Example - fill TX FIFO with 2 words (8 bytes) **`0xDEAD BEEF`**, **`0x0102 0304`**: - - Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 -------|------|------|------|------|------|------|------|------|------ - TX | 0x40 | 0xDE | 0xAD | 0xBE | 0xEF | 0x01 | 0x02 | 0x03 | 0x04 - RX | x | x | x | x | x | x | x | x | x - -*x = don't care* - -#### Read from RX FIFO (**0x50**) - -Reads data from RX FIFO. Data can be read only as 4 byte (32 bit) words. Before reading it's necessary to check RX FIFO availability. Reading empty FIFO won't break anything but it's pointless. - -Command bytes: - - Byte(s) | 0 | 1:4 | 5:8 | 9:12 | ... ------------|------|-----------|-----------|-----------|----- - Value | 0x50 | Data word | Data word | Data word | ... - Direction | W | R | R | R | ... - -Data word bits: - - 31:0 | -------| - DATA | - -- Bits 31:0 **DATA**: Word read from the bus. - -Example - read 2 words (8 bytes) **`0xDEAD BEEF`**, **`0x0102 0304`** from RX FIFO: - - Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 -------|------|------|------|------|------|------|------|------|------ - TX | 0x50 | x | x | x | x | x | x | x | x - RX | x | 0xDE | 0xAD | 0xBE | 0xEF | 0x01 | 0x02 | 0x03 | 0x04 - -*x = don't care* - -#### Read debug FIFO status (**0x60**) (unimplemented) - -Reads debug FIFO status. - -Command bytes: - - Byte(s) | 0 | 1:4 ------------|------|------------------------ - Value | 0x00 | Debug FIFO status word - Direction | W | R - -Debug FIFO status word bits: - - 31:22 | 21:11 | 10:0 --------|------------|------------ - x | TX_FIFO_UB | RX_FIFO_UB - -- Bits 31:22: Reserved. Reads as 0. -- Bits 21:11 **TX_FIFO_UB**: Number of bytes waiting to be processed by N64. -- Bits 10:0 **RX_FIFO_UB**: Number of bytes waiting to be read by PC. - -Example - read debug FIFO status word: - - Byte | 0 | 1 | 2 | 3 | 4 -------|------|------|------|------|------ - TX | 0x60 | x | x | x | x - RX | x | 0x00 | 0x00 | 0x00 | 0x00 - -*x = don't care* - -#### Write to debug TX FIFO (**0x70**) (unimplemented) - -Writes bytes to debug TX FIFO. - -This command consumes space in debug TX FIFO. Check debug TX FIFO availability before issuing this command. - -Command bytes: - - Byte(s) | 0 | 1 | 2 | 3 | ... ------------|------|-----------|-----------|-----------|----- - Value | 0x70 | Data byte | Data byte | Data byte | ... - Direction | W | W | W | W | ... - -Data byte bits: - - 7:0 | -------| - DATA | - -- Bits 7:0 **DATA**: Byte to be written to debug TX FIFO. - -Example - fill debug TX FIFO with 4 bytes **`0xDE`**, **`0xAD`**, **`0xBE`**, **`0xEF`**: - - Byte | 0 | 1 | 2 | 3 | 4 -------|------|------|------|------|------ - TX | 0x70 | 0xDE | 0xAD | 0xBE | 0xEF - RX | x | x | x | x | x - -*x = don't care* - -#### Read from debug RX FIFO (**0x80**) (unimplemented) - -Reads bytes from debug RX FIFO. Before reading it's necessary to check debug RX FIFO availability. Reading empty FIFO won't break anything but it's pointless. -Command bytes: - - Byte(s) | 0 | 1 | 2 | 3 | ... ------------|------|-----------|-----------|-----------|----- - Value | 0x70 | Data byte | Data byte | Data byte | ... - Direction | W | R | R | R | ... - -Data byte bits: - - 7:0 | -------| - DATA | - -- Bits 7:0 **DATA**: Byte read from debug RX FIFO. - -Example - read 4 bytes from debug RX FIFO: - - Byte | 0 | 1 | 2 | 3 | 4 -------|------|------|------|------|------ - TX | 0x80 | x | x | x | x - RX | x | 0xDE | 0xAD | 0xBE | 0xEF - -*x = don't care* - -#### PC communication module bus controller reset (**0xFC**) - -Resets PC communication module bus controller. It's recommended to issue "Reset (**0xFF**)" command before sending this one. - -This command consumes space in TX FIFO. Check TX FIFO availability before issuing this command. - -Command bytes: - - Byte(s) | 0 | 1:4 ------------|------|------------ - Value | 0xFC | Reset word - Direction | W | W - -Reset word bits: - - 31:0 | -------| - x | - -- Bits 31:0: Reserved. Writes are ignored but it's recommended to be set as 0 for future compatibility. - -Example - reset PC communication module: - - Byte | 0 | 1 | 2 | 3 | 4 -------|------|------|------|------|------ - TX | 0xFC | 0x00 | 0x00 | 0x00 | 0x00 - RX | x | x | x | x | x - -*x = don't care* - -#### Flush TX FIFO (**0xFD**) - -Flushes TX FIFO. - -Command bytes: - - Byte(s) | 0 ------------|------ - Value | 0xFD - Direction | W - -Example - flush TX FIFO: - - Byte | 0 -------|------ - TX | 0xFD - RX | x - -*x = don't care* - -#### Flush RX FIFO (**0xFE**) - -Flushes RX FIFO. - -Command bytes: - - Byte(s) | 0 ------------|------ - Value | 0xFE - Direction | W - -Example - flush RX FIFO: - - Byte | 0 -------|------ - TX | 0xFE - RX | x - -*x = don't care* - -#### Reset (**0xFF**) - -Resets PC communication module SPI controller and flushes both TX and RX data/debug FIFOs. - -Command bytes: - - Byte(s) | 0 ------------|------ - Value | 0xFF - Direction | W - -Example - send reset: - - Byte | 0 -------|------ - TX | 0xFF - RX | x - -*x = don't care* diff --git a/fw/SummerCart64.qsf b/fw/SummerCart64.qsf index 6b926cc..a9c8447 100644 --- a/fw/SummerCart64.qsf +++ b/fw/SummerCart64.qsf @@ -19,7 +19,7 @@ # # Quartus Prime # Version 20.1.0 Build 711 06/05/2020 SJ Lite Edition -# Date created = 23:45:19 July 29, 2020 +# Date created = 00:00:00 November 06, 2020 # # -------------------------------------------------------------------------- # # @@ -37,30 +37,35 @@ # -------------------------------------------------------------------------- # -set_global_assignment -name FAMILY "MAX 10" -set_global_assignment -name DEVICE 10M08SCE144C8G -set_global_assignment -name TOP_LEVEL_ENTITY top + +# Project-Wide Assignments +# ======================== set_global_assignment -name ORIGINAL_QUARTUS_VERSION 20.1.0 set_global_assignment -name PROJECT_CREATION_TIME_DATE "23:45:19 JULY 29, 2020" set_global_assignment -name LAST_QUARTUS_VERSION "20.1.0 Lite Edition" set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files -set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 -set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 -set_global_assignment -name DEVICE_FILTER_PACKAGE EQFP -set_global_assignment -name DEVICE_FILTER_PIN_COUNT 144 -set_global_assignment -name DEVICE_FILTER_SPEED_GRADE 8 -set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 256 -set_global_assignment -name ENABLE_OCT_DONE OFF -set_global_assignment -name ENABLE_CONFIGURATION_PINS OFF -set_global_assignment -name ENABLE_BOOT_SEL_PIN OFF -set_global_assignment -name EXTERNAL_FLASH_FALLBACK_ADDRESS 00000000 -set_global_assignment -name USE_CONFIGURATION_DEVICE OFF -set_global_assignment -name CRC_ERROR_OPEN_DRAIN OFF -set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -rise -set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -fall -set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -rise -set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -fall -set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "3.3-V LVTTL" +set_global_assignment -name FLOW_ENABLE_POWER_ANALYZER ON +set_global_assignment -name SMART_RECOMPILE OFF +set_global_assignment -name VERILOG_FILE rtl/cart/control.v +set_global_assignment -name VERILOG_FILE rtl/usb/pc.v +set_global_assignment -name VERILOG_FILE rtl/usb/ftdi_fsi.v +set_global_assignment -name QSYS_FILE rtl/intel/flash/onchip_flash.qsys +set_global_assignment -name VERILOG_FILE rtl/glue/device_arbiter.v +set_global_assignment -name SYSTEMVERILOG_FILE rtl/intel/gpio/gpio_ddro/altera_gpio_lite.sv -library gpio_ddro +set_global_assignment -name QIP_FILE rtl/intel/gpio/gpio_ddro.qip +set_global_assignment -name QIP_FILE rtl/intel/pll/pll.qip +set_global_assignment -name QIP_FILE rtl/intel/ram/ram_n64_eeprom.qip +set_global_assignment -name VERILOG_FILE rtl/memory/sdram.v +set_global_assignment -name VERILOG_FILE rtl/memory/embedded_flash.v +set_global_assignment -name VERILOG_FILE rtl/n64/si.v +set_global_assignment -name VERILOG_FILE rtl/n64/pi.v +set_global_assignment -name VERILOG_FILE rtl/n64/bank_decoder.v +set_global_assignment -name VERILOG_FILE rtl/top.v +set_global_assignment -name SDC_FILE constraints.sdc +set_global_assignment -name QIP_FILE rtl/intel/fifo/fifo_ack.qip + +# Pin & Location Assignments +# ========================== set_location_assignment PIN_26 -to i_clk set_location_assignment PIN_98 -to io_sdram_dq[0] set_location_assignment PIN_97 -to io_sdram_dq[1] @@ -140,18 +145,6 @@ set_location_assignment PIN_52 -to io_n64_pi_ad[12] set_location_assignment PIN_55 -to io_n64_pi_ad[13] set_location_assignment PIN_57 -to io_n64_pi_ad[14] set_location_assignment PIN_59 -to io_n64_pi_ad[15] -set_location_assignment PIN_13 -to i_ftdi_do -set_location_assignment PIN_14 -to o_ftdi_di -set_location_assignment PIN_12 -to i_ftdi_clk -set_location_assignment PIN_15 -to i_ftdi_cs -set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW" -set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)" -set_global_assignment -name FLOW_ENABLE_POWER_ANALYZER ON -set_global_assignment -name POWER_DEFAULT_INPUT_IO_TOGGLE_RATE "12.5 %" -set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top -set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top -set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top -set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to o_sdram_clk set_location_assignment PIN_29 -to i_n64_cic_clk set_location_assignment PIN_30 -to io_n64_cic_dq set_location_assignment PIN_138 -to io_pmod[0] @@ -165,46 +158,121 @@ set_location_assignment PIN_11 -to io_pmod[7] set_location_assignment PIN_22 -to io_rtc_sda set_location_assignment PIN_21 -to o_rtc_scl set_location_assignment PIN_17 -to o_led -set_global_assignment -name OPTIMIZATION_MODE BALANCED -set_global_assignment -name ENABLE_SIGNALTAP ON -set_global_assignment -name USE_SIGNALTAP_FILE misc/SignalTapLogicAnalyzer.stp +set_location_assignment PIN_15 -to i_ftdi_cts +set_location_assignment PIN_14 -to i_ftdi_so +set_location_assignment PIN_13 -to o_ftdi_clk +set_location_assignment PIN_12 -to o_ftdi_si + +# Classic Timing Assignments +# ========================== +set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 +set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 +set_global_assignment -name TIMING_ANALYZER_MULTICORNER_ANALYSIS ON +set_global_assignment -name TIMING_ANALYZER_DO_REPORT_TIMING ON + +# Compiler Assignments +# ==================== +set_global_assignment -name OPTIMIZATION_MODE "HIGH PERFORMANCE EFFORT" + +# Analysis & Synthesis Assignments +# ================================ +set_global_assignment -name FAMILY "MAX 10" +set_global_assignment -name DEVICE_FILTER_PACKAGE EQFP +set_global_assignment -name DEVICE_FILTER_PIN_COUNT 144 +set_global_assignment -name DEVICE_FILTER_SPEED_GRADE 8 +set_global_assignment -name VERILOG_INPUT_VERSION SYSTEMVERILOG_2005 +set_global_assignment -name VERILOG_SHOW_LMF_MAPPING_MESSAGES OFF +set_global_assignment -name TOP_LEVEL_ENTITY top + +# Fitter Assignments +# ================== +set_global_assignment -name DEVICE 10M08SCE144C8G +set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 256 +set_global_assignment -name ENABLE_CONFIGURATION_PINS OFF +set_global_assignment -name ENABLE_BOOT_SEL_PIN OFF +set_global_assignment -name CRC_ERROR_OPEN_DRAIN OFF +set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "3.3-V LVTTL" set_global_assignment -name RESERVE_ALL_UNUSED_PINS_WEAK_PULLUP "AS INPUT TRI-STATED WITH WEAK PULL-UP" set_global_assignment -name INTERNAL_FLASH_UPDATE_MODE "SINGLE COMP IMAGE" -set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to io_n64_si_dq -set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to io_n64_cic_dq -set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_pi_aleh -set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_pi_read -set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_pi_write -set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_pi_alel -set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_cic_clk -set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_ftdi_do -set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_si_clk -set_global_assignment -name SMART_RECOMPILE OFF -set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to i_clk -set_global_assignment -name TIMING_ANALYZER_MULTICORNER_ANALYSIS ON -set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to i_ftdi_clk -set_global_assignment -name TIMING_ANALYZER_DO_REPORT_TIMING ON set_global_assignment -name PHYSICAL_SYNTHESIS_ASYNCHRONOUS_SIGNAL_PIPELINING OFF set_global_assignment -name ROUTER_CLOCKING_TOPOLOGY_ANALYSIS ON -set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to "pll:sys_pll|altpll:altpll_component|pll_altpll:auto_generated|wire_pll1_clk[0]" -set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to "pll:sys_pll|altpll:altpll_component|pll_altpll:auto_generated|wire_pll1_clk[1]" set_global_assignment -name FITTER_EFFORT "STANDARD FIT" -set_global_assignment -name VERILOG_FILE rtl/n64_si.v -set_global_assignment -name VERILOG_FILE rtl/cart_config.v -set_global_assignment -name VERILOG_FILE rtl/sdram.v -set_global_assignment -name VERILOG_FILE rtl/external/wbsdram.v -set_global_assignment -name VERILOG_FILE rtl/external/qflexpress.v -set_global_assignment -name VERILOG_FILE rtl/address_decoder.v -set_global_assignment -name VERILOG_FILE rtl/flash.v -set_global_assignment -name VERILOG_FILE rtl/n64_pi.v -set_global_assignment -name VERILOG_FILE rtl/pc.v -set_global_assignment -name VERILOG_FILE rtl/top.v -set_global_assignment -name SDC_FILE constraints.sdc -set_global_assignment -name QIP_FILE rtl/intel/pll/pll.qip -set_global_assignment -name QIP_FILE rtl/intel/gpio/gpio_ddro.qip -set_global_assignment -name QIP_FILE rtl/intel/fifo/fifo_bus_to_pc.qip -set_global_assignment -name QIP_FILE rtl/intel/fifo/fifo_pc_to_bus.qip -set_global_assignment -name SIGNALTAP_FILE misc/SignalTapLogicAnalyzer.stp -set_global_assignment -name QIP_FILE rtl/intel/ram/ram_n64_eeprom.qip -set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top -set_global_assignment -name SLD_FILE db/SignalTapLogicAnalyzer_auto_stripped.stp \ No newline at end of file + +# Assembler Assignments +# ===================== +set_global_assignment -name ENABLE_OCT_DONE OFF +set_global_assignment -name EXTERNAL_FLASH_FALLBACK_ADDRESS 00000000 +set_global_assignment -name USE_CONFIGURATION_DEVICE OFF + +# Programmer Assignments +# ====================== +set_global_assignment -name GENERATE_SVF_FILE ON + +# Signal Tap Assignments +# ====================== +set_global_assignment -name ENABLE_SIGNALTAP OFF +set_global_assignment -name USE_SIGNALTAP_FILE output_files/signal_tap_logic_analyzer.stp + +# Power Estimation Assignments +# ============================ +set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "NO HEAT SINK WITH STILL AIR" +set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)" +set_global_assignment -name POWER_DEFAULT_INPUT_IO_TOGGLE_RATE "12.5 %" + +# Advanced I/O Timing Assignments +# =============================== +set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -rise +set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -fall +set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -rise +set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -fall + +# ------------------------------ +# start ENTITY(altera_gpio_lite) + + # Project-Wide Assignments + # ======================== + +# end ENTITY(altera_gpio_lite) +# ---------------------------- + +# ----------------------- +# start ENTITY(gpio_ddro) + + # Project-Wide Assignments + # ======================== + +# end ENTITY(gpio_ddro) +# --------------------- + +# ----------------- +# start ENTITY(top) + + # Fitter Assignments + # ================== + set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to io_n64_si_dq + set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to io_n64_cic_dq + set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_pi_aleh + set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_pi_read + set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_pi_write + set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_pi_alel + set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_cic_clk + set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_si_clk + set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to i_clk + set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to "pll:sys_pll|altpll:altpll_component|pll_altpll:auto_generated|wire_pll1_clk[0]" + set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to "pll:sys_pll|altpll:altpll_component|pll_altpll:auto_generated|wire_pll1_clk[1]" + + # start DESIGN_PARTITION(Top) + # --------------------------- + + # Incremental Compilation Assignments + # =================================== + set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top + set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top + set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top + set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top + + # end DESIGN_PARTITION(Top) + # ------------------------- + +# end ENTITY(top) +# --------------- \ No newline at end of file diff --git a/fw/constraints.sdc b/fw/constraints.sdc index 2749b28..420709b 100644 --- a/fw/constraints.sdc +++ b/fw/constraints.sdc @@ -1,7 +1,5 @@ create_clock -name i_clk -period 20 [get_ports i_clk] -create_clock -name i_ftdi_clk -period 33.333 [get_ports i_ftdi_clk] - derive_pll_clocks -create_base_clocks derive_clock_uncertainty diff --git a/fw/misc/SignalTapLogicAnalyzer.stp b/fw/misc/SignalTapLogicAnalyzer.stp deleted file mode 100644 index 52f1cdf..0000000 --- a/fw/misc/SignalTapLogicAnalyzer.stp +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 'r_n64_reset_ff2' == high - - - - - - 11 - 11 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fw/rtl/address_decoder.v b/fw/rtl/address_decoder.v deleted file mode 100644 index 6d52e15..0000000 --- a/fw/rtl/address_decoder.v +++ /dev/null @@ -1,44 +0,0 @@ -module address_decoder ( - input [31:0] i_address, - - output o_cart_config, - output o_flash, - output o_flash_cfg, - output o_sdram, - output o_eeprom, - - input i_flash_enable, - input i_sdram_enable, - input i_eeprom_pi_enable, - - output o_address_valid -); - - localparam CART_CONFIG_ADDR = 32'h1E00_0000; - localparam CART_CONFIG_WIDTH = 8; - - assign o_cart_config = i_address[31:CART_CONFIG_WIDTH] == CART_CONFIG_ADDR[31:CART_CONFIG_WIDTH]; - - localparam FLASH_ADDR = 32'h1000_0000; - localparam FLASH_REMAP_ADDR = 32'h1800_0000; - localparam FLASH_WIDTH = 24; - - assign o_flash = i_flash_enable && (i_address[31:FLASH_WIDTH] == (i_sdram_enable ? FLASH_REMAP_ADDR[31:FLASH_WIDTH] : FLASH_ADDR[31:FLASH_WIDTH])); - - localparam FLASH_CFG_ADDR = 32'h1C00_0000; - - assign o_flash_cfg = i_flash_enable && (i_address == FLASH_CFG_ADDR); - - localparam SDRAM_ADDR = 32'h1000_0000; - localparam SDRAM_WIDTH = 26; - - assign o_sdram = i_sdram_enable && (i_address[31:SDRAM_WIDTH] == SDRAM_ADDR[31:SDRAM_WIDTH]); - - localparam EEPROM_ADDR = 32'h1D00_0000; - localparam EEPROM_WIDTH = 11; - - assign o_eeprom = i_eeprom_pi_enable && (i_address[31:EEPROM_WIDTH] == EEPROM_ADDR[31:EEPROM_WIDTH]); - - assign o_address_valid = (|{o_cart_config, o_flash, o_flash_cfg, o_sdram, o_eeprom}); - -endmodule diff --git a/fw/rtl/cart/control.v b/fw/rtl/cart/control.v new file mode 100644 index 0000000..581f792 --- /dev/null +++ b/fw/rtl/cart/control.v @@ -0,0 +1,77 @@ +module cart_control ( + input i_clk, + input i_reset, + + input i_n64_reset, + input i_n64_nmi, + + input i_request, + input i_write, + output o_busy, + output reg o_ack, + input [0:0] i_address, + output reg [31:0] o_data, + input [31:0] i_data, + + output reg o_rom_switch, + output reg o_eeprom_enable, + output reg o_eeprom_16k_mode +); + + // Input synchronization + + reg r_reset_ff1, r_reset_ff2; + reg r_nmi_ff1, r_nmi_ff2; + + always @(posedge i_clk) begin + {r_reset_ff2, r_reset_ff1} <= {r_reset_ff1, i_n64_reset}; + {r_nmi_ff2, r_nmi_ff1} <= {r_nmi_ff1, i_n64_nmi}; + end + + + // Registers + + reg [31:0] r_bootloader; + + + // Bus controller + + assign o_busy = 1'b0; + + always @(posedge i_clk) begin + if (i_reset) begin + o_ack <= 1'b0; + end else begin + o_ack <= i_request && !i_write && !o_busy; + end + end + + always @(posedge i_clk) begin + if (i_reset) begin + o_rom_switch <= 1'b0; + o_eeprom_enable <= 1'b0; + o_eeprom_16k_mode <= 1'b0; + r_bootloader <= 32'h0000_0000; + end else begin + if (i_request && i_write && !o_busy) begin + case (i_address) + 1'd0: {o_eeprom_16k_mode, o_eeprom_enable, o_rom_switch} <= {i_data[4], i_data[3], i_data[1]}; + 1'd1: r_bootloader <= i_data; + endcase + end + if (!r_reset_ff2 || !r_nmi_ff2) begin + o_rom_switch <= 1'b0; + end + end + end + + always @(posedge i_clk) begin + if (i_request && !i_write && !o_busy) begin + case (i_address) + 1'd0: o_data <= {o_eeprom_16k_mode, o_eeprom_enable, 1'b0, o_rom_switch, 1'b0}; + 1'd1: o_data <= r_bootloader; + endcase + end + end + +endmodule diff --git a/fw/rtl/cart_config.v b/fw/rtl/cart_config.v deleted file mode 100644 index 45b09c7..0000000 --- a/fw/rtl/cart_config.v +++ /dev/null @@ -1,67 +0,0 @@ -module cart_config ( - input i_clk, - input i_reset, - - input i_n64_reset, - input i_n64_nmi, - - input i_select, - input i_read_rq, - input i_write_rq, - output reg o_ack, - input [31:0] i_address, - input [31:0] i_data, - output reg [31:0] o_data, - - input i_n64_disabled, - - output o_flash_enable, - output o_sdram_enable, - output o_eeprom_pi_enable, - output o_eeprom_enable, - output o_eeprom_16k_enable -); - - reg [4:0] r_cart_config; - reg [7:0] r_cic_type; - - always @(*) begin - o_data = 32'd0; - if (!i_address[2]) o_data = {27'd0, r_cart_config}; - if (i_address[2]) o_data = {24'd0, r_cic_type}; - end - - assign { - o_eeprom_16k_enable, - o_eeprom_enable, - o_eeprom_pi_enable, - o_sdram_enable, - o_flash_enable, - } = r_cart_config; - - reg r_last_n64_reset; - reg r_last_n64_nmi; - - wire w_n64_reset_op = !i_n64_disabled && ((!r_last_n64_reset && i_n64_reset) || (!r_last_n64_nmi && i_n64_nmi)); - - always @(posedge i_clk) begin - r_last_n64_reset <= i_n64_reset; - r_last_n64_nmi <= i_n64_nmi; - end - - always @(posedge i_clk) begin - if (i_reset) r_cart_config[4:0] <= 5'b00001; - if (w_n64_reset_op) r_cart_config[2:0] <= 3'b001; - if (!i_reset && i_select && i_write_rq && !i_address[2]) r_cart_config <= i_data[4:0]; - end - - always @(posedge i_clk) begin - if (i_reset) r_cic_type <= 8'd0; - if (!i_reset && i_select && i_write_rq && i_address[2]) r_cic_type <= i_data[7:0]; - end - - always @(posedge i_clk) begin - o_ack <= !i_reset && i_select && (i_read_rq || i_write_rq); - end - -endmodule diff --git a/fw/rtl/external/README.md b/fw/rtl/external/README.md deleted file mode 100644 index 8cd088b..0000000 --- a/fw/rtl/external/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# External code for SummerCart64 Firmware - -This folder contains code downloaded directly from external repositories with minor modifications. - -## qflexpress.v - -### Source - -[https://github.com/ZipCPU/qspiflash/blob/master/rtl/qflexpress.v](https://github.com/ZipCPU/qspiflash/blob/master/rtl/qflexpress.v) - -### Documentation - -There's [comprehensive documentation](https://zipcpu.com/blog/2019/03/27/qflexpress.html) for this module: - -### Modifications - -- Changed initialization sequence. -- Extended address space for 16-bit address alignment. - -## wbsdram.v - -### Source - -[https://github.com/ZipCPU/arrowzip/blob/master/rtl/arrowzip/wbsdram.v](https://github.com/ZipCPU/arrowzip/blob/master/rtl/arrowzip/wbsdram.v) - -### Modifications - -- Changed refresh interval that suits 90 MHz clock and used SDRAM chip. -- Changed column and row address widths. diff --git a/fw/rtl/external/qflexpress.v b/fw/rtl/external/qflexpress.v deleted file mode 100644 index 9daca83..0000000 --- a/fw/rtl/external/qflexpress.v +++ /dev/null @@ -1,2239 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Filename: qflexpress.v -// -// Project: A Set of Wishbone Controlled SPI Flash Controllers -// -// Purpose: To provide wishbone controlled read access (and read access -// *only*) to the QSPI flash, using a flash clock equal to the -// system clock, and nothing more. Indeed, this is designed to be a -// *very* stripped down version of a flash driver, with the goal of -// providing 1) very fast access for 2) very low logic count. -// -// Three modes/states of operation: -// 1. Startup/maintenance, places the device in the Quad XIP mode -// 2. Normal operations, takes 12+8N clocks to read a value -// 3. Configuration--useful to allow an external controller issue erase -// or program commands (or other) without requiring us to -// clutter up the logic with a giant state machine -// -// STARTUP -// 1. Waits for the flash to come on line -// Start out idle for 300 uS -// 2. Sends a signal to remove the flash from any QSPI read mode. In our -// case, we'll send several clocks of an empty command. In SPI -// mode, it'll get ignored. In QSPI mode, it'll remove us from -// QSPI mode. -// 3. Explicitly places and leaves the flash into QSPI mode -// 0xEB 3(0x00) 0xa0 6(0x00) -// 4. All done -// -// Creator: Dan Gisselquist, Ph.D. -// Gisselquist Technology, LLC -// -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2018-2019, Gisselquist Technology, LLC -// -// This file is part of the set of Wishbone controlled SPI flash controllers -// project -// -// The Wishbone SPI flash controller project is free software (firmware): -// you can redistribute it and/or modify it under the terms of the GNU Lesser -// General Public License as published by the Free Software Foundation, either -// version 3 of the License, or (at your option) any later version. -// -// The Wishbone SPI flash controller project is distributed in the hope -// that it will be useful, but WITHOUT ANY WARRANTY; without even the implied -// warranty of MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. (It's in the $(ROOT)/doc directory. Run make -// with no target there if the PDF file isn't present.) If not, see -// for a copy. -// -// License: LGPL, v3, as defined and found on www.gnu.org, -// http://www.gnu.org/licenses/lgpl.html -// -// -//////////////////////////////////////////////////////////////////////////////// -// -// -`default_nettype none -// -// 290 raw, 372 w/ pipe, 410 cfg, 499 cfg w/pipe -module qflexpress(i_clk, i_reset, - i_wb_cyc, i_wb_stb, i_cfg_stb, i_wb_we, i_wb_addr, i_wb_data, - o_wb_ack, o_wb_stall, o_wb_data, - o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat); - // - // LGFLASHSZ is the size of the flash memory. It defines the number - // of bits in the address register and more. This controller will support - // flash sizes up to 2^LGFLASHSZ, where LGFLASHSZ goes up to 32. - parameter LGFLASHSZ=24; - // - // OPT_PIPE makes it possible to string multiple requests together, - // with no intervening need to shutdown the QSPI connection and send a - // new address - parameter [0:0] OPT_PIPE = 1'b1; - // - // OPT_CFG enables the configuration logic port, and hence the - // ability to erase and program the flash, as well as the ability - // to perform other commands such as read-manufacturer ID, adjust - // configuration registers, etc. - parameter [0:0] OPT_CFG = 1'b1; - // - // OPT_STARTUP enables the startup logic - parameter [0:0] OPT_STARTUP = 1'b1; - // - // OPT_ADDR32 enables 32 bit addressing, rather than 24bit - // Control this by controlling the LGFLASHSZ parameter above. Anything - // greater than 24 will use 32-bit addressing, otherwise the regular - // 24-bit addressing - localparam [0:0] OPT_ADDR32 = (LGFLASHSZ > 24); - // - parameter OPT_CLKDIV = 0; - // - // Normally, I place the first byte read from the flash, and the lowest - // flash address, into bits [7:0], and then shift it up--to where upon - // return it is found in bits [31:24]. This is ideal for a big endian - // systems, not so much for little endian systems. The endian swap - // allows the bus to swap the return values in order to support little - // endian systems. - parameter [0:0] OPT_ENDIANSWAP = 1'b1; - // - // OPT_ODDR will be true any time the clock has no clock division - localparam [0:0] OPT_ODDR = (OPT_CLKDIV == 0); - // - // CKDV_BITS is the number of bits necessary to represent a counter - // that can do the CLKDIV division - localparam CKDV_BITS = (OPT_CLKDIV == 0) ? 0 - : ((OPT_CLKDIV < 2) ? 1 - : ((OPT_CLKDIV < 4) ? 2 - : ((OPT_CLKDIV < 8) ? 3 - : ((OPT_CLKDIV < 16) ? 4 - : ((OPT_CLKDIV < 32) ? 5 - : ((OPT_CLKDIV < 64) ? 6 - : ((OPT_CLKDIV < 128) ? 7 - : ((OPT_CLKDIV < 256) ? 8 : 9)))))))); - // - // RDDELAY is the number of clock cycles from when o_qspi_dat is valid - // until i_qspi_dat is valid. Read delays from 0-4 have been verified. - // DDR Registered I/O on a Xilinx device can be done with a RDDELAY=3 - // On Intel/Altera devices, RDDELAY=2 works - // I'm using RDDELAY=0 for my iCE40 devices - // - parameter RDDELAY = 0; - // - // NDUMMY is the number of "dummy" clock cycles between the 24-bits of - // the Quad I/O address and the first data bits. This includes the - // two clocks of the Quad output mode byte, 0xa0. The default is 10 - // for a Micron device. Windbond seems to want 2. Note your flash - // device carefully when you choose this value. - // - parameter NDUMMY = 6; - // - // For dealing with multiple flash devices, the OPT_STARTUP_FILE allows - // a hex file to be provided containing the necessary script to place - // the design into the proper initial configuration. - parameter OPT_STARTUP_FILE=""; - // - // - // - // - localparam [4:0] CFG_MODE = 12; - localparam [4:0] QSPEED_BIT = 11; - localparam [4:0] DSPEED_BIT = 10; // Not supported - localparam [4:0] DIR_BIT = 9; - localparam [4:0] USER_CS_n = 8; - // - localparam [1:0] NORMAL_SPI = 2'b00; - localparam [1:0] QUAD_WRITE = 2'b10; - localparam [1:0] QUAD_READ = 2'b11; - // localparam [7:0] DIO_READ_CMD = 8'hbb; - localparam [7:0] QIO_READ_CMD = OPT_ADDR32 ? 8'hec : 8'heb; - // - localparam AW=LGFLASHSZ-1; - localparam DW=32; - // -`ifdef FORMAL - localparam F_LGDEPTH=$clog2(3+RDDELAY+(OPT_ADDR32 ? 2:0)); - reg f_past_valid; -`endif - // - // - input wire i_clk, i_reset; - // - input wire i_wb_cyc, i_wb_stb, i_cfg_stb, i_wb_we; - input wire [(AW-1):0] i_wb_addr; - input wire [(DW-1):0] i_wb_data; - // - output reg o_wb_ack, o_wb_stall; - output reg [(DW-1):0] o_wb_data; - // - output reg o_qspi_sck; - output reg o_qspi_cs_n; - output reg [1:0] o_qspi_mod; - output wire [3:0] o_qspi_dat; - input wire [3:0] i_qspi_dat; - // - // Debugging port - // output wire o_dbg_trigger; - // output wire [31:0] o_debug; - - reg dly_ack, read_sck, xtra_stall; - // clk_ctr must have enough bits for ... - // 8 address clocks, 4-bits each - // NDUMMY dummy clocks, including two mode bytes - // 8 data clocks - // (RDDELAY clocks not counted here) - reg [4:0] clk_ctr; - - // - // User override logic - // - reg cfg_mode, cfg_speed, cfg_dir, cfg_cs; - wire cfg_write, cfg_hs_write, cfg_ls_write, cfg_hs_read, - user_request, bus_request, pipe_req, cfg_noop, cfg_stb; - // - assign bus_request = (i_wb_stb)&&(!o_wb_stall) - &&(!i_wb_we)&&(!cfg_mode); - assign cfg_stb = (OPT_CFG)&&(i_cfg_stb)&&(!o_wb_stall); - assign cfg_noop = ((cfg_stb)&&((!i_wb_we)||(!i_wb_data[CFG_MODE]) - ||(i_wb_data[USER_CS_n]))) - ||((!OPT_CFG)&&(i_cfg_stb)&&(!o_wb_stall)); - assign user_request = (cfg_stb)&&(i_wb_we)&&(i_wb_data[CFG_MODE]); - - assign cfg_write = (user_request)&&(!i_wb_data[USER_CS_n]); - assign cfg_hs_write = (cfg_write)&&(i_wb_data[QSPEED_BIT]) - &&(i_wb_data[DIR_BIT]); - assign cfg_hs_read = (cfg_write)&&(i_wb_data[QSPEED_BIT]) - &&(!i_wb_data[DIR_BIT]); - assign cfg_ls_write = (cfg_write)&&(!i_wb_data[QSPEED_BIT]); - - - reg ckstb, ckpos, ckneg, ckpre; - reg maintenance; - reg [1:0] m_mod; - reg m_cs_n; - reg m_clk; - reg [3:0] m_dat; - - - generate if (OPT_ODDR) - begin - - always @(*) - begin - ckstb = 1'b1; - ckpos = 1'b1; - ckneg = 1'b1; - ckpre = 1'b1; - end - - end else if (OPT_CLKDIV == 1) - begin : CKSTB_ONE - - reg clk_counter; - - initial clk_counter = 1'b1; - always @(posedge i_clk) - if (i_reset) - clk_counter <= 1'b1; - else if (clk_counter != 0) - clk_counter <= 1'b0; - else if (bus_request) - clk_counter <= (pipe_req); - else if ((maintenance)||(!o_qspi_cs_n && o_wb_stall)) - clk_counter <= 1'b1; - - always @(*) - begin - ckpre = (clk_counter == 1); - ckstb = (clk_counter == 0); - ckpos = (clk_counter == 1); - ckneg = (clk_counter == 0); - end - - end else begin : CKSTB_GEN - - reg [CKDV_BITS-1:0] clk_counter; - - initial clk_counter = OPT_CLKDIV; - always @(posedge i_clk) - if (i_reset) - clk_counter <= OPT_CLKDIV; - else if (clk_counter != 0) - clk_counter <= clk_counter - 1; - else if (bus_request) - clk_counter <= (pipe_req ? OPT_CLKDIV : 0); - else if ((maintenance)||(!o_qspi_cs_n && o_wb_stall)) - clk_counter <= OPT_CLKDIV; - - initial ckpre = (OPT_CLKDIV == 1); - initial ckstb = 1'b0; - initial ckpos = (OPT_CLKDIV == 1); - always @(posedge i_clk) - if (i_reset) - begin - ckpre <= (OPT_CLKDIV == 1); - ckstb <= 1'b0; - ckpos <= (OPT_CLKDIV == 1); - end else // if (OPT_CLKDIV > 1) - begin - ckpre <= (clk_counter == 2); - ckstb <= (clk_counter == 1); - ckpos <= (clk_counter == (OPT_CLKDIV+1)/2+1); - end - - always @(*) - ckneg = ckstb; -`ifdef FORMAL - always @(*) - assert(!ckpos || !ckneg); - - always @(posedge i_clk) - if ((f_past_valid)&&(!$past(i_reset))&&($past(ckpre))) - assert(ckstb); -`endif - end endgenerate - - // - // - // Maintenance / startup portion - // - // - generate if (OPT_STARTUP) - begin : GEN_STARTUP - localparam M_WAITBIT=10; - localparam M_LGADDR=5; -`ifdef FORMAL - // For formal, jump into the middle of the startup - localparam M_FIRSTIDX=9; -`else - localparam M_FIRSTIDX=0; -`endif - reg [M_WAITBIT:0] m_this_word; - reg [M_WAITBIT:0] m_cmd_word [0:(1< 3) - m_counter <= 3; -`endif - end - end else begin - m_midcount <= (m_counter > 1); - if (m_counter > 0) - m_counter <= m_counter - 1'b1; - end - - initial m_cs_n = 1'b1; - initial m_mod = NORMAL_SPI; - always @(posedge i_clk) - if (i_reset) - begin - m_cs_n <= 1'b1; - m_mod <= NORMAL_SPI; - m_bitcount <= 0; - end else if (ckstb) - begin - if (m_bitcount != 0) - m_bitcount <= m_bitcount - 1; - else if ((m_ce)&&(m_final)) - begin - m_cs_n <= 1'b1; - m_mod <= NORMAL_SPI; - m_bitcount <= 0; - end else if ((m_midcount)||(m_this_word[M_WAITBIT])) - begin - m_cs_n <= 1'b1; - m_mod <= NORMAL_SPI; - m_bitcount <= 0; - end else begin - m_cs_n <= 1'b0; - m_mod <= m_this_word[M_WAITBIT-1:M_WAITBIT-2]; - m_bitcount <= (!OPT_ODDR && m_cs_n) ? 4'h2 : 4'h1; - if (!m_this_word[M_WAITBIT-1]) - m_bitcount <= (!OPT_ODDR && m_cs_n) ? 4'h8 : 4'h7;//i.e.7 - end - end - - always @(posedge i_clk) - if (m_ce) - begin - if (m_bitcount == 0) - begin - if (!OPT_ODDR && m_cs_n) - begin - m_dat <= {(4){m_this_word[7]}}; - m_byte <= m_this_word[7:0]; - end else begin - m_dat <= m_this_word[7:4]; - m_byte <= { m_this_word[3:0], 4'h0}; - if (!m_this_word[M_WAITBIT-1]) - begin - // Slow speed - m_dat[0] <= m_this_word[7]; - m_byte <= { m_this_word[6:0], 1'b0 }; - end - end - end else begin - m_dat <= m_byte[7:4]; - m_byte <= { m_byte[3:0], 4'h0 }; - if (!m_mod[1]) - begin - // Slow speed - m_dat[0] <= m_byte[7]; - m_byte <= { m_byte[6:0], 1'b0 }; - end else begin - m_byte <= { m_byte[3:0], 4'b00 }; - end - end - end - - if (OPT_ODDR) - begin - always @(*) - m_clk = !m_cs_n; - end else begin - - always @(posedge i_clk) - if (i_reset) - m_clk <= 1'b1; - else if (m_cs_n) - m_clk <= 1'b1; - else if ((!m_clk)&&(ckpos)) - m_clk <= 1'b1; - else if (m_midcount) - m_clk <= 1'b1; - else if (new_word && m_this_word[M_WAITBIT]) - m_clk <= 1'b1; - else if (ckneg) - m_clk <= 1'b0; - end - -`ifdef FORMAL - (* anyconst *) reg [M_LGADDR:0] f_const_addr; - - always @(*) - begin - assert((m_cmd_word[f_const_addr][M_WAITBIT]) - ||(m_cmd_word[f_const_addr][9:8] != 2'b01)); - if (m_cmd_word[f_const_addr][M_WAITBIT]) - assert(m_cmd_word[f_const_addr][M_WAITBIT-3:0] > 0); - end - always @(*) - begin - if (m_cmd_index != f_const_addr) - assume((m_cmd_word[m_cmd_index][M_WAITBIT])||(m_cmd_word[m_cmd_index][9:8] != 2'b01)); - if (m_cmd_word[m_cmd_index][M_WAITBIT]) - assume(m_cmd_word[m_cmd_index][M_WAITBIT-3:0]>0); - end - - always @(*) - begin - assert((m_this_word[M_WAITBIT]) - ||(m_this_word[9:8] != 2'b01)); - if (m_this_word[M_WAITBIT]) - assert(m_this_word[M_WAITBIT-3:0] > 0); - end - - // Setting the last two command words to IDLE with maximum - // counts is required by our implementation - always @(*) - assert(m_cmd_word[5'h1e] == 11'h7ff); - always @(*) - assert(m_cmd_word[5'h1f] == 11'h7ff); - - wire [M_LGADDR-1:0] last_index; - assign last_index = m_cmd_index - 1; - - always @(posedge i_clk) - if ((f_past_valid)&&(m_cmd_index != M_FIRSTIDX)) - assert(m_this_word == m_cmd_word[last_index]); - - always @(posedge i_clk) - assert(m_midcount == (m_counter != 0)); - - always @(posedge i_clk) - begin - cover(!maintenance); - cover(m_cmd_index == 5'h0a); - cover(m_cmd_index == 5'h0b); - cover(m_cmd_index == 5'h0c); - cover(m_cmd_index == 5'h0d); - cover(m_cmd_index == 5'h0e); - cover(m_cmd_index == 5'h0f); - cover(m_cmd_index == 5'h10); - cover(m_cmd_index == 5'h11); - cover(m_cmd_index == 5'h12); - cover(m_cmd_index == 5'h13); - cover(m_cmd_index == 5'h14); - cover(m_cmd_index == 5'h15); - cover(m_cmd_index == 5'h16); - cover(m_cmd_index == 5'h17); - cover(m_cmd_index == 5'h18); - cover(m_cmd_index == 5'h19); - cover(m_cmd_index == 5'h1a); - cover(m_cmd_index == 5'h1b); - cover(m_cmd_index == 5'h1c); - cover(m_cmd_index == 5'h1d); - cover(m_cmd_index == 5'h1e); - cover(m_cmd_index == 5'h1f); - end - - reg [M_WAITBIT:0] f_last_word; - reg [8:0] f_mspi; - reg [2:0] f_mqspi; - - initial f_last_word = -1; - always @(posedge i_clk) - if (i_reset) - f_last_word = -1; - else if (new_word) - f_last_word <= m_this_word; - - - initial f_mspi = 0; - always @(posedge i_clk) - if (i_reset) - f_mspi <= 0; - else if (ckstb) begin - f_mspi <= f_mspi << 1; - if (maintenance && !m_final && new_word - &&(!m_this_word[M_WAITBIT]) - &&(m_this_word[9:8] == NORMAL_SPI)) - begin - if (m_cs_n && !OPT_ODDR) - f_mspi[0] <= 1'b1; - else - f_mspi[1] <= 1'b1; - end - end - - initial f_mqspi = 0; - always @(posedge i_clk) - if (i_reset) - f_mqspi <= 0; - else if (ckstb) begin - f_mqspi <= f_mqspi << 1; - if (maintenance && !m_final && new_word - &&(!m_this_word[M_WAITBIT]) - &&(m_this_word[9])) - begin - if (m_cs_n && !OPT_ODDR) - f_mqspi[0] <= 1'b1; - else - f_mqspi[1] <= 1'b1; - end - end - - always @(*) - if (OPT_ODDR) - assert(!f_mspi[0] && !f_mqspi[0]); - - - always @(*) - if ((|f_mspi) || (|f_mqspi)) - begin - - assert(maintenance); - assert(!m_cs_n); - assert(m_mod == f_last_word[9:8]); - assert(m_midcount == 1'b0); - - end else if (maintenance && o_qspi_cs_n) - begin - assert(f_last_word[M_WAITBIT]); - assert(m_counter <= f_last_word[M_WAITBIT-1:0]); - assert(m_midcount == (m_counter != 0)); - assert(m_cs_n); - end - - always @(*) - assert((f_mspi == 0)||(f_mqspi == 0)); - - always @(*) - if (|f_mspi) - assert(m_mod == NORMAL_SPI); - - always @(*) - case(f_mspi[8:1]) - 8'h00: begin end - 8'h01: assert(m_dat[0] == f_last_word[7]); - 8'h02: assert(m_dat[0] == f_last_word[6]); - 8'h04: assert(m_dat[0] == f_last_word[5]); - 8'h08: assert(m_dat[0] == f_last_word[4]); - 8'h10: assert(m_dat[0] == f_last_word[3]); - 8'h20: assert(m_dat[0] == f_last_word[2]); - 8'h40: assert(m_dat[0] == f_last_word[1]); - 8'h80: assert(m_dat[0] == f_last_word[0]); - default: begin assert(0); end - endcase - - always @(*) - if (|f_mqspi) - assert(m_mod == QUAD_WRITE || m_mod == QUAD_READ); - - always @(*) - case(f_mqspi[2:1]) - 2'b00: begin end - 2'b01: assert(m_dat[3:0] == f_last_word[7:4]); - 2'b10: assert(m_dat[3:0] == f_last_word[3:0]); - default: begin assert(f_mqspi != 2'b11); end - endcase -`endif - end else begin : NO_STARTUP_OPT - - always @(*) - begin - maintenance = 0; - m_mod = 2'b00; - m_cs_n = 1'b1; - m_clk = 1'b0; - m_dat = 4'h0; - end - - // verilator lint_off UNUSED - wire [8:0] unused_maintenance; - assign unused_maintenance = { maintenance, - m_mod, m_cs_n, m_clk, m_dat }; - // verilator lint_on UNUSED - end endgenerate - - - reg [32+(OPT_ADDR32 ? 8:0)+4*(OPT_ODDR ? 0:1)-1:0] data_pipe; - reg pre_ack = 1'b0; - reg actual_sck; - - // - // - // Data / access portion - // - // - initial data_pipe = 0; - always @(posedge i_clk) - begin - if (!o_wb_stall) - begin - // Set the high bits to zero initially - data_pipe <= 0; - - data_pipe[8+LGFLASHSZ-1:0] <= { - i_wb_addr, 1'b0, 4'ha, 4'h0 }; - - if (i_cfg_stb) - // High speed configuration I/O - data_pipe[24+(OPT_ADDR32 ? 8:0) +: 8] <= i_wb_data[7:0]; - - if ((i_cfg_stb)&&(!i_wb_data[QSPEED_BIT])) - begin // Low speed configuration I/O - data_pipe[28+(OPT_ADDR32 ? 8:0)]<= i_wb_data[7]; - data_pipe[24+(OPT_ADDR32 ? 8:0)]<= i_wb_data[6]; - end - - if (i_cfg_stb) - begin // These can be set independent of speed - data_pipe[20+(OPT_ADDR32 ? 8:0)]<= i_wb_data[5]; - data_pipe[16+(OPT_ADDR32 ? 8:0)]<= i_wb_data[4]; - data_pipe[12+(OPT_ADDR32 ? 8:0)]<= i_wb_data[3]; - data_pipe[ 8+(OPT_ADDR32 ? 8:0)]<= i_wb_data[2]; - data_pipe[ 4+(OPT_ADDR32 ? 8:0)]<= i_wb_data[1]; - data_pipe[ 0+(OPT_ADDR32 ? 8:0)]<= i_wb_data[0]; - end - end else if (ckstb) - data_pipe <= { data_pipe[(32+(OPT_ADDR32 ? 8:0)+4*((OPT_ODDR ? 0:1)-1))-1:0], 4'h0 }; - - if (maintenance) - data_pipe[28+(OPT_ADDR32 ? 8:0)+4*(OPT_ODDR ? 0:1) +: 4] <= m_dat; - end - - assign o_qspi_dat = data_pipe[28+(OPT_ADDR32 ? 8:0)+4*(OPT_ODDR ? 0:1) +: 4]; - - // Since we can't abort any transaction once started, without - // risking losing XIP mode or any other mode we might be in, we'll - // keep track of whether this operation should be ack'd upon - // completion - always @(posedge i_clk) - if ((i_reset)||(!i_wb_cyc)) - pre_ack <= 1'b0; - else if ((bus_request)||(cfg_write)) - pre_ack <= 1'b1; - - generate if (OPT_PIPE) - begin : OPT_PIPE_BLOCK - reg r_pipe_req; - wire w_pipe_condition; - - reg [(AW-1):0] next_addr; - always @(posedge i_clk) - if (!o_wb_stall) - next_addr <= i_wb_addr + 2'd2; - - assign w_pipe_condition = (i_wb_stb)&&(!i_wb_we)&&(pre_ack) - &&(!maintenance) - &&(!cfg_mode) - &&(!o_qspi_cs_n) - &&(|clk_ctr[2:0]) - &&(next_addr == i_wb_addr); - - initial r_pipe_req = 1'b0; - always @(posedge i_clk) - if ((clk_ctr == 1)&&(ckstb)) - r_pipe_req <= 1'b0; - else - r_pipe_req <= w_pipe_condition; - - assign pipe_req = r_pipe_req; - end else begin - assign pipe_req = 1'b0; - end endgenerate - - - initial clk_ctr = 0; - always @(posedge i_clk) - if ((i_reset)||(maintenance)) - clk_ctr <= 0; - else if ((bus_request)&&(!pipe_req)) - // Notice that this is only for - // regular bus reads, and so the check for - // !pipe_req - clk_ctr <= 5'd14 + NDUMMY + (OPT_ADDR32 ? 2:0)+(OPT_ODDR ? 0:1); - else if (bus_request) // && pipe_req - // Otherwise, if this is a piped read, we'll - // reset the counter back to eight. - clk_ctr <= 5'd8; - else if (cfg_ls_write) - clk_ctr <= 5'd8 + ((OPT_ODDR) ? 0:1); - else if (cfg_write) - clk_ctr <= 5'd2 + ((OPT_ODDR) ? 0:1); - else if ((ckstb)&&(|clk_ctr)) - clk_ctr <= clk_ctr - 1'b1; - - initial o_qspi_sck = (!OPT_ODDR); - always @(posedge i_clk) - if (i_reset) - o_qspi_sck <= (!OPT_ODDR); - else if (maintenance) - o_qspi_sck <= m_clk; - else if ((!OPT_ODDR)&&(bus_request)&&(pipe_req)) - o_qspi_sck <= 1'b0; - else if ((bus_request)||(cfg_write)) - o_qspi_sck <= 1'b1; - else if (OPT_ODDR) - begin - if ((cfg_mode)&&(clk_ctr <= 1)) - // Config mode has no pipe instructions - o_qspi_sck <= 1'b0; - else if (clk_ctr[4:0] > 5'd1) - o_qspi_sck <= 1'b1; - else - o_qspi_sck <= 1'b0; - end else if (((ckpos)&&(!o_qspi_sck))||(o_qspi_cs_n)) - begin - o_qspi_sck <= 1'b1; - end else if ((ckneg)&&(o_qspi_sck)) begin - - if ((cfg_mode)&&(clk_ctr <= 1)) - // Config mode has no pipe instructions - o_qspi_sck <= 1'b1; - else if (clk_ctr[4:0] > 5'd1) - o_qspi_sck <= 1'b0; - else - o_qspi_sck <= 1'b1; - end - - initial o_qspi_cs_n = 1'b1; - always @(posedge i_clk) - if (i_reset) - o_qspi_cs_n <= 1'b1; - else if (maintenance) - o_qspi_cs_n <= m_cs_n; - else if ((cfg_stb)&&(i_wb_we)) - o_qspi_cs_n <= (!i_wb_data[CFG_MODE])||(i_wb_data[USER_CS_n]); - else if ((OPT_CFG)&&(cfg_cs)) - o_qspi_cs_n <= 1'b0; - else if ((bus_request)||(cfg_write)) - o_qspi_cs_n <= 1'b0; - else if (ckstb) - o_qspi_cs_n <= (clk_ctr <= 1); - - // Control the mode of the external pins - // NORMAL_SPI: i_miso is an input, o_mosi is an output - // QUAD_READ: i_miso is an input, o_mosi is an input - // QUAD_WRITE: i_miso is an output, o_mosi is an output - initial o_qspi_mod = NORMAL_SPI; - always @(posedge i_clk) - if (i_reset) - o_qspi_mod <= NORMAL_SPI; - else if (maintenance) - o_qspi_mod <= m_mod; - else if ((bus_request)&&(!pipe_req)) - o_qspi_mod <= QUAD_WRITE; - else if ((bus_request)||(cfg_hs_read)) - o_qspi_mod <= QUAD_READ; - else if (cfg_hs_write) - o_qspi_mod <= QUAD_WRITE; - else if ((cfg_ls_write)||((cfg_mode)&&(!cfg_speed))) - o_qspi_mod <= NORMAL_SPI; - else if ((ckstb)&&(clk_ctr <= 5'd9)&&((!cfg_mode)||(!cfg_dir))) - o_qspi_mod <= QUAD_READ; - - initial o_wb_stall = 1'b1; - always @(posedge i_clk) - if (i_reset) - o_wb_stall <= 1'b1; - else if (maintenance) - o_wb_stall <= 1'b1; - else if ((RDDELAY > 0)&&((i_cfg_stb)||(i_wb_stb))&&(!o_wb_stall)) - o_wb_stall <= 1'b1; - else if ((RDDELAY == 0)&&((cfg_write)||(bus_request))) - o_wb_stall <= 1'b1; - else if (ckstb || clk_ctr == 0) - begin - if (ckpre && (i_wb_stb)&&(pipe_req)&&(clk_ctr == 5'd2)) - o_wb_stall <= 1'b0; - else if ((clk_ctr > 1)||(xtra_stall)) - o_wb_stall <= 1'b1; - else - o_wb_stall <= 1'b0; - end else if (ckpre && (i_wb_stb)&&(pipe_req)&&(clk_ctr == 5'd1)) - o_wb_stall <= 1'b0; - - initial dly_ack = 1'b0; - always @(posedge i_clk) - if (i_reset) - dly_ack <= 1'b0; - else if ((ckstb)&&(clk_ctr == 1)) - dly_ack <= (i_wb_cyc)&&(pre_ack); - else if ((i_wb_stb)&&(!o_wb_stall)&&(!bus_request)) - dly_ack <= 1'b1; - else if (cfg_noop) - dly_ack <= 1'b1; - else - dly_ack <= 1'b0; - - generate if (OPT_ODDR) - begin : SCK_ACTUAL - - always @(*) - actual_sck = o_qspi_sck; - - end else if (OPT_CLKDIV == 1) - begin : SCK_ONE - - initial actual_sck = 1'b0; - always @(posedge i_clk) - if (i_reset) - actual_sck <= 1'b0; - else - actual_sck <= (!o_qspi_sck)&&(clk_ctr > 0); - - end else begin : SCK_ANY - - initial actual_sck = 1'b0; - always @(posedge i_clk) - if (i_reset) - actual_sck <= 1'b0; - else - actual_sck <= (o_qspi_sck)&&(ckpre)&&(clk_ctr > 0); - - end endgenerate - - -`ifdef FORMAL - reg [F_LGDEPTH-1:0] f_extra; -`endif - - generate if (RDDELAY == 0) - begin : RDDELAY_NONE - - always @(*) - begin - read_sck = actual_sck; - o_wb_ack = dly_ack; - xtra_stall = 1'b0; - end - -`ifdef FORMAL - always @(*) - f_extra = 0; -`endif - - end else - begin : RDDELAY_NONZERO - - reg [RDDELAY-1:0] sck_pipe, ack_pipe, stall_pipe; - reg not_done; - - initial sck_pipe = 0; - initial ack_pipe = 0; - initial stall_pipe = -1; - if (RDDELAY > 1) - begin - always @(posedge i_clk) - if (i_reset) - sck_pipe <= 0; - else - sck_pipe <= { sck_pipe[RDDELAY-2:0], actual_sck }; - - always @(posedge i_clk) - if (i_reset || !i_wb_cyc) - ack_pipe <= 0; - else - ack_pipe <= { ack_pipe[RDDELAY-2:0], dly_ack }; - - always @(posedge i_clk) - if (i_reset) - stall_pipe <= -1; - else - stall_pipe <= { stall_pipe[RDDELAY-2:0], not_done }; - - - end else // if (RDDELAY > 0) - begin - always @(posedge i_clk) - if (i_reset) - sck_pipe <= 0; - else - sck_pipe <= actual_sck; - - always @(posedge i_clk) - if (i_reset || !i_wb_cyc) - ack_pipe <= 0; - else - ack_pipe <= dly_ack; - - always @(posedge i_clk) - if (i_reset) - stall_pipe <= -1; - else - stall_pipe <= not_done; - end - - always @(*) - begin - not_done = (i_wb_stb || i_cfg_stb) && !o_wb_stall; - if (clk_ctr > 1) - not_done = 1'b1; - if ((clk_ctr == 1)&&(!ckstb)) - not_done = 1'b1; - end - - always @(*) - o_wb_ack = ack_pipe[RDDELAY-1]; - - always @(*) - read_sck = sck_pipe[RDDELAY-1]; - - always @(*) - xtra_stall = |stall_pipe; - -`ifdef FORMAL - integer k; - always @(*) - if (!i_wb_cyc) - f_extra = 0; - else begin - f_extra = 0; - for(k=0; k 0) - begin - always @(posedge i_clk) - assume(i_qspi_dat == $past(dly_idat,RDDELAY)); - end else begin - always @(posedge i_clk) - assume(i_qspi_dat == dly_idat); - end endgenerate - - // - //////////////////////////////////////////////////////////////////////// - // - // Maintenance mode assertions - // - - always @(*) - if (maintenance) - begin - assume((!i_wb_stb)&&(!i_cfg_stb)); - - assert(f_outstanding == 0); - - assert(o_wb_stall); - // - assert(clk_ctr == 0); - assert(cfg_mode == 1'b0); - end - - always @(*) - if (maintenance) - begin - assert(clk_ctr == 0); - assert(!o_wb_ack); - end - - //////////////////////////////////////////////////////////////////////// - // - // - // - always @(posedge i_clk) - if (dly_ack) - assert(clk_ctr[2:0] == 0); - - // Zero cycle requests - always @(posedge i_clk) - if ((f_past_valid)&&(!$past(i_reset))&&(($past(cfg_noop)) - ||($past(i_wb_stb && i_wb_we && !o_wb_stall)))) - assert((dly_ack)&&((!i_wb_cyc) - ||(f_outstanding == 1 + f_extra))); - - always @(posedge i_clk) - if ((f_outstanding > 0)&&(clk_ctr > 0)) - assert(pre_ack); - - always @(posedge i_clk) - if ((i_wb_cyc)&&(dly_ack)) - assert(f_outstanding >= 1 + f_extra); - - always @(posedge i_clk) - if ((f_past_valid)&&(clk_ctr == 0)&&(!dly_ack) - &&((!$past(i_wb_stb|i_cfg_stb))||($past(o_wb_stall)))) - assert(f_outstanding == f_extra); - - always @(*) - if ((i_wb_cyc)&&(pre_ack)&&(!o_qspi_cs_n)) - assert((f_outstanding >= 1 + f_extra)||((OPT_CFG)&&(cfg_mode))); - - always @(*) - if ((cfg_mode)&&(!dly_ack)&&(clk_ctr == 0)) - assert(f_outstanding == f_extra); - - always @(*) - if (cfg_mode) - assert(f_outstanding <= 1 + f_extra); - - ///////////////// - // - // Idle channel - // - ///////////////// - always @(*) - if (!maintenance) - begin - if (o_qspi_cs_n) - begin - assert(clk_ctr == 0); - assert(o_qspi_sck == !OPT_ODDR); - end else if (clk_ctr == 0) - assert(o_qspi_sck == !OPT_ODDR); - end - - always @(*) - assert(o_qspi_mod != 2'b01); - - always @(*) - if (clk_ctr > (5'h8 * (1+OPT_CLKDIV))) - begin - assert(!cfg_mode); - assert(!cfg_cs); - end - - - always @(posedge i_clk) - if ((OPT_CLKDIV==1)&&(!o_qspi_cs_n)&&(!$past(o_qspi_cs_n)) - &&(!$past(o_qspi_cs_n,2))&&(!cfg_mode)) - assert(o_qspi_sck != $past(o_qspi_sck)); - - ///////////////// - // - // Read requests - // - ///////////////// - always @(posedge i_clk) - if ((f_past_valid)&&(!$past(i_reset))&&($past(bus_request))) - begin - assert(!o_qspi_cs_n); - if ((OPT_ODDR)||(!$past(pipe_req))) - assert(o_qspi_sck == 1'b1); - else - assert(o_qspi_sck == 1'b0); - // - if (!$past(o_qspi_cs_n)) - begin - assert(clk_ctr == 5'd8); - assert(o_qspi_mod == QUAD_READ); - end else begin - assert(clk_ctr == 5'd14 + NDUMMY - + (OPT_ADDR32 ? 2:0) + (OPT_ODDR ? 0:1)); - assert(o_qspi_mod == QUAD_WRITE); - end - end - - always @(*) - assert(clk_ctr <= 5'd18 + NDUMMY + (OPT_ODDR ? 0:1)); - - always @(*) - if ((OPT_ODDR)&&(!o_qspi_cs_n)) - assert((o_qspi_sck)||(actual_sck)||(cfg_mode)||(maintenance)); - - always @(*) - if ((RDDELAY == 0)&&((dly_ack)&&(clk_ctr == 0))) - assert(!o_wb_stall); - - always @(*) - if (!maintenance) - begin - if (cfg_mode) - begin - if (!cfg_cs) - assert(o_qspi_cs_n); - else if (!cfg_speed) - assert(o_qspi_mod == NORMAL_SPI); - else if ((cfg_dir)&&(clk_ctr > 0)) - assert(o_qspi_mod == QUAD_WRITE); - end else if (clk_ctr > 5'd8) - assert(o_qspi_mod == QUAD_WRITE); - else if (clk_ctr > 0) - assert(o_qspi_mod == QUAD_READ); - end - - always @(posedge i_clk) - if (((!OPT_PIPE)&&(clk_ctr != 0))||(clk_ctr > 5'd1)) - assert(o_wb_stall); - - always @(posedge i_clk) - if ((OPT_CLKDIV>0)&&($past(o_qspi_cs_n))) - assert(o_qspi_sck); - - ///////////////// - // - // User mode - // - ///////////////// - always @(*) - if ((maintenance)||(!OPT_CFG)) - assert(!cfg_mode); - always @(*) - if ((OPT_CFG)&&(cfg_mode)) - assert(o_qspi_cs_n == !cfg_cs); - else - assert(!cfg_cs); - - // - // - // - // - always @(posedge i_clk) - if (bus_request) - begin - // Make sure all of the bits are set - fv_addr <= 0; - // Now set as many bits as we have address bits - fv_addr[AW-1:0] <= i_wb_addr; - end - - always @(posedge i_clk) - if ((i_wb_stb || i_cfg_stb) && !o_wb_stall && i_wb_we) - fv_data <= i_wb_data; - - // Memory reads - - initial f_memread = 0; - generate if (RDDELAY == 0) - begin - - always @(posedge i_clk) - if (i_reset) - f_memread <= 0; - else begin - if (ckstb) - f_memread <= { f_memread[F_MEMACK-1:0],1'b0}; - else if (!OPT_ODDR) - f_memread[F_MEMACK] <= 1'b0; - if ((bus_request)&&(o_qspi_cs_n)) - f_memread[0] <= 1'b1; - end - end else begin - - always @(posedge i_clk) - if (i_reset) - f_memread <= 0; - else begin - if (ckstb) - f_memread <= { f_memread[F_MEMACK-1:0],1'b0}; - else if (!OPT_ODDR) - f_memread[F_MEMACK:F_MEMDONE] - <= { f_memread[F_MEMACK-1:F_MEMDONE],1'b0}; - if ((bus_request)&&(o_qspi_cs_n)) - f_memread[0] <= 1'b1; - end - end endgenerate - - always @(posedge i_clk) - if ((OPT_ODDR)&&(|f_memread[F_MEMDONE-1:0])) - assert(o_qspi_sck); - - always @(posedge i_clk) - if (|f_memread[6+(OPT_ADDR32 ? 2:0)+(OPT_ODDR ? 0:1):0]) - assert(o_qspi_mod == QUAD_WRITE); - else if (|f_memread[(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0) +7 +: NDUMMY]) - // begin assert(1); end - begin end - else if (|f_memread) - assert(o_qspi_mod == QUAD_READ); - - generate if (RDDELAY > 0) - begin - always @(posedge i_clk) - if ($past(ckpos,RDDELAY)) - begin - if ($past(o_qspi_mod,RDDELAY) == NORMAL_SPI) - f_past_data <= { f_past_data[31:0], i_qspi_dat[1] }; - else if ($past(o_qspi_mod,RDDELAY) == QUAD_READ) - f_past_data <= { f_past_data[28:0], i_qspi_dat[3:0] }; - end - end else begin - always @(posedge i_clk) - if (ckpos) - begin - if (o_qspi_mod == NORMAL_SPI) - f_past_data <= { f_past_data[31:0], i_qspi_dat[1] }; - else if (o_qspi_mod == QUAD_READ) - f_past_data <= { f_past_data[28:0], i_qspi_dat[3:0] }; - end - end endgenerate - - - always @(posedge i_clk) - if (|f_memread[(OPT_ODDR ? 0:1) +: 7 + (OPT_ADDR32 ? 2:0)]) - begin - if (OPT_ADDR32) - begin - // Eight extra bits of address - if (f_memread[(OPT_ODDR ? 0:1)]) - assert(o_qspi_dat== fv_addr[29:26]); - if (f_memread[1 + (OPT_ODDR ? 0:1)]) - assert(o_qspi_dat== fv_addr[25:22]); - end - // 6 nibbles of address, one nibble of mode - if (f_memread[(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) - assert(o_qspi_dat== fv_addr[21:18]); - if (f_memread[1+(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) - assert(o_qspi_dat== fv_addr[17:14]); - if (f_memread[2+(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) - assert(o_qspi_dat== fv_addr[13:10]); - if (f_memread[3+(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) - assert(o_qspi_dat== fv_addr[ 9: 6]); - if (f_memread[4+(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) - assert(o_qspi_dat== fv_addr[ 5: 2]); - if (f_memread[5+(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) - assert(o_qspi_dat=={ fv_addr[1:0],2'b00 }); - if (f_memread[6+(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) - assert(o_qspi_dat == 4'ha); - end - - always @(posedge i_clk) - if (OPT_ODDR) - begin - if (f_memread[F_MEMACK] && OPT_ENDIANSWAP) - begin - assert(o_wb_data[ 7: 4] == $past(i_qspi_dat,8)); - assert(o_wb_data[ 3: 0] == $past(i_qspi_dat,7)); - assert(o_wb_data[15:12] == $past(i_qspi_dat,6)); - assert(o_wb_data[11: 8] == $past(i_qspi_dat,5)); - assert(o_wb_data[23:20] == $past(i_qspi_dat,4)); - assert(o_wb_data[19:16] == $past(i_qspi_dat,3)); - assert(o_wb_data[31:28] == $past(i_qspi_dat,2)); - assert(o_wb_data[27:24] == $past(i_qspi_dat,1)); - end else if (f_memread[F_MEMACK]) - begin - assert(o_wb_data[31:28] == $past(i_qspi_dat,8)); - assert(o_wb_data[27:24] == $past(i_qspi_dat,7)); - assert(o_wb_data[23:20] == $past(i_qspi_dat,6)); - assert(o_wb_data[19:16] == $past(i_qspi_dat,5)); - assert(o_wb_data[15:12] == $past(i_qspi_dat,4)); - assert(o_wb_data[11: 8] == $past(i_qspi_dat,3)); - assert(o_wb_data[ 7: 4] == $past(i_qspi_dat,2)); - assert(o_wb_data[ 3: 0] == $past(i_qspi_dat,1)); - end else if (|f_memread) - begin - if (!OPT_PIPE) - assert(o_wb_stall); - else if (!f_memread[F_MEMDONE-1]) - assert(o_wb_stall); - assert(!o_wb_ack); - end - end else if (f_memread[F_MEMACK] && OPT_ENDIANSWAP) - assert((!o_wb_ack)||( - o_wb_data[ 7: 0] == f_past_data[31:24] - && o_wb_data[15: 8] == f_past_data[23:16] - && o_wb_data[23:16] == f_past_data[15: 8] - && o_wb_data[31:24] == f_past_data[ 7: 0])); - else if (f_memread[F_MEMACK]) // 25 - assert((!o_wb_ack)||(o_wb_data == f_past_data[31:0])); - else if (|f_memread) - begin - if ((!OPT_PIPE)||(!ckstb)) - assert(o_wb_stall); - else if (!f_memread[F_MEMDONE-1]) - assert(o_wb_stall); - assert(!o_wb_ack); - end - - always @(posedge i_clk) - if (f_memread[F_MEMDONE]) - assert((clk_ctr == 0)||((OPT_PIPE)&&(clk_ctr == F_PIPEDONE))); - // else if (|f_memread[F_MEMDONE-1:0]) - // assert(f_memread[F_MEMDONE-clk_ctr]); - - generate for(k=0; k 0) - begin - - always @(posedge i_clk) - if (|f_cfghswrite[F_CFGHSACK:F_CFGHSDONE]) - assert(!actual_sck); - - end endgenerate - - always @(posedge i_clk) - if (|f_cfghswrite) - begin - assert(!o_qspi_cs_n); - assert(o_qspi_mod == QUAD_WRITE); - end - - always @(posedge i_clk) - if (|f_cfghswrite[1:0]) - begin - assert(!dly_ack); - assert(o_wb_stall); - end - - always @(posedge i_clk) - if (f_cfghswrite[F_CFGHSACK]) - begin - assert((o_wb_ack)||(!$past(pre_ack))||(!$past(i_wb_cyc))); - assert(o_qspi_mod == QUAD_WRITE); - assert(!o_wb_stall); - end else if (|f_cfghswrite) - begin - assert(o_wb_stall); - assert(!o_wb_ack); - end - - //////////////////////////////////////////////////////////////////////// - // - // High speed config read - // - initial f_cfghsread = 0; - always @(posedge i_clk) - if (i_reset) - f_cfghsread <= 0; - else begin - if (ckstb) - f_cfghsread <= { f_cfghsread[F_CFGHSACK-1:0], 1'b0 }; - else if (!OPT_ODDR) - f_cfghsread[F_CFGHSACK:F_CFGHSDONE] - <={f_cfghsread[F_CFGHSACK:F_CFGHSDONE], 1'b0}; - if (cfg_hs_read) - f_cfghsread[0] <= 1'b1; - end - - always @(*) - if (OPT_ODDR) - begin - if (|f_cfghsread[1:0]) - assert(o_qspi_sck); - else if (|f_cfghsread) - assert(!o_qspi_sck); - end - - always @(posedge i_clk) - if (|f_cfghsread[F_CFGHSACK:F_CFGHSDONE]) - assert(o_qspi_sck == !OPT_ODDR); - - always @(*) - if ((!maintenance)&&(o_qspi_cs_n)) - assert(!actual_sck); - - always @(posedge i_clk) - if (|f_cfghsread[F_CFGHSDONE-1:F_CFGHSDONE-2]) - begin - assert(!dly_ack); - assert(!o_qspi_cs_n); - assert(o_qspi_mod == QUAD_READ); - assert(o_wb_stall); - end - - generate if (RDDELAY > 0) - begin - - always @(posedge i_clk) - if (|f_cfghswrite[F_CFGHSACK:F_CFGHSDONE]) - assert(!actual_sck); - - end endgenerate - - - always @(posedge i_clk) - if (f_cfghsread[F_CFGHSACK]) - begin - if (OPT_ODDR) - begin - assert(o_wb_data[7:4] == $past(i_qspi_dat[3:0],2)); - assert(o_wb_data[3:0] == $past(i_qspi_dat[3:0],1)); - end - assert(o_wb_data[7:0] == f_past_data[7:0]); - assert((o_wb_ack)||(!$past(pre_ack))||(!$past(i_wb_cyc))); - assert(o_qspi_mod == QUAD_READ); - assert(!o_wb_stall); - end else if (|f_cfghsread) - begin - assert(o_wb_stall); - assert(!o_wb_ack); - end - - //////////////////////////////////////////////////////////////////////// - // - // Crossover checks - // - wire f_qspi_not_done, f_qspi_not_ackd, f_qspi_active, f_qspi_ack; - assign f_qspi_not_done = - (|f_memread[F_MEMDONE-1:0]) - ||(|f_piperead[F_PIPEDONE-1:0]) - ||(|f_cfglswrite[F_CFGLSDONE-1:0]) - ||(|f_cfghswrite[F_CFGHSDONE-1:0]) - ||(|f_cfghsread[F_CFGHSDONE-1:0]); - assign f_qspi_active = (!maintenance)&&( - (|f_memread[F_MEMACK-1:0]) - ||(|f_piperead[F_PIPEACK-1:0]) - ||(|f_cfglswrite[F_CFGLSACK-1:0]) - ||(|f_cfghswrite[F_CFGHSACK-1:0]) - ||(|f_cfghsread[F_CFGHSACK-1:0])); - assign f_qspi_not_ackd = (!maintenance)&&(!f_qspi_not_done)&&( - (|f_memread[F_MEMACK-1:0]) - ||(|f_piperead[F_PIPEACK-1:0]) - ||(|f_cfglswrite[F_CFGLSACK-1:0]) - ||(|f_cfghswrite[F_CFGHSACK-1:0]) - ||(|f_cfghsread[F_CFGHSACK-1:0])); - assign f_qspi_ack = (!maintenance)&& - (|f_memread[F_MEMACK:0]) - ||(|f_piperead[F_PIPEACK:0]) - ||(|f_cfglswrite[F_CFGLSACK:0]) - ||(|f_cfghswrite[F_CFGHSACK:0]) - ||(|f_cfghsread[F_CFGHSACK:0]); - - always @(*) - begin - if ((|f_memread[F_MEMDONE:0])||(|f_piperead[F_PIPEDONE:0])) - begin - assert(f_cfglswrite == 0); - assert(f_cfghswrite == 0); - assert(f_cfghsread == 0); - end - - if (|f_cfglswrite[F_CFGLSDONE:0]) - begin - assert(f_cfghswrite[F_CFGHSDONE:0] == 0); - assert(f_cfghsread[F_CFGHSDONE:0] == 0); - end - - if (|f_cfghswrite[F_CFGHSDONE:0]) - assert(f_cfghsread[F_CFGHSDONE:0] == 0); - - if (maintenance) - begin - assert(f_memread == 0); - assert(f_piperead == 0); - assert(f_cfglswrite == 0); - assert(f_cfghswrite == 0); - assert(f_cfghsread == 0); - end - - assert(clk_ctr <= F_MEMDONE); - end - - always @(posedge i_clk) - if ((f_past_valid)&&(!f_qspi_ack)&&(!$past(i_reset)) - &&(!$past(maintenance))) - begin - assert($stable(o_wb_data[7:0])); - if (!cfg_mode && !$past(cfg_mode) - && !$past(i_cfg_stb && !o_wb_stall) - &&($past(f_past_valid)) - && !$past(i_cfg_stb && !o_wb_stall,2)) - assert($stable(o_wb_data)); - end - - always @(*) - if (!maintenance && actual_sck) - begin - assert(f_qspi_not_done); - /* - assert((|f_memread[F_MEMDONE:0]) - ||(|f_piperead[F_PIPEDONE:0]) - ||(|f_cfglswrite[F_CFGLSDONE:0]) - ||(|f_cfghswrite[F_CFGHSDONE:0]) - ||(|f_cfghsread[F_CFGHSDONE:0])); - */ - end - - always @(*) - if (!maintenance && !o_qspi_cs_n && !cfg_mode) - begin - assert((|f_memread[F_MEMDONE:0]) - ||(|f_piperead[F_PIPEDONE:0])); - end else if (!maintenance && cfg_mode) - begin - if ((o_qspi_sck == OPT_ODDR)||(clk_ctr > 0)||(actual_sck)) - begin - assert( (|f_cfglswrite[F_CFGLSDONE-1:0]) - ||(|f_cfghswrite[F_CFGHSDONE-1:0]) - ||(|f_cfghsread[F_CFGHSDONE-1:0])); - end - end - - always @(posedge i_clk) - if (o_wb_ack && !$past((i_wb_stb || i_cfg_stb)&&!o_wb_stall, 1+RDDELAY)) - begin - assert(f_memread[F_MEMACK] - || f_piperead[F_PIPEACK] - || f_cfglswrite[F_CFGLSACK] - || f_cfghswrite[F_CFGHSACK] - || f_cfghsread[F_CFGHSACK]); - end - - - //////////////////////////////////////////////////////////////////////// - // - // Cover Properties - // - //////////////////////////////////////////////////////////////////////// - // - // Due to the way the chip starts up, requiring 32k+ maintenance clocks, - // these cover statements are not likely to be hit - - generate if (!OPT_STARTUP) - begin - // Why is this generate only if !OPT_STARTUP? For performance - // reasons. The startup sequence can take a great many clocks. - // cover() would never be able to work through all of those - // clocks to find the following examples, and so it would fail. - // By only checking these cover() statements if !OPT_STARTUP, - // we give them an opportunity to succeed - always @(posedge i_clk) - cover(o_wb_ack && f_memread[ F_MEMACK]); - - if (OPT_CFG) - begin - always @(posedge i_clk) - begin - cover(cfg_ls_write); - cover(cfg_hs_write); - cover(cfg_hs_read); - - cover(|f_cfglswrite); - cover(|f_cfghsread); - cover(|f_cfghswrite); - - cover(o_wb_ack && |f_cfglswrite); - cover(o_wb_ack && |f_cfghsread); - cover(o_wb_ack && |f_cfghswrite); - - cover(f_cfglswrite[0]); - cover(f_cfghsread[ 0]); - cover(f_cfghswrite[0]); - - cover(f_cfglswrite[F_CFGLSACK-2]); - cover(f_cfghsread[ F_CFGHSACK-2]); - cover(f_cfghswrite[F_CFGHSACK-2]); - - cover(f_cfglswrite[F_CFGLSACK-1]); - cover(f_cfghsread[ F_CFGHSACK-1]); - cover(f_cfghswrite[F_CFGHSACK-1]); - - cover(f_cfglswrite[F_CFGLSACK]); - cover(f_cfghsread[ F_CFGHSACK]); - cover(f_cfghswrite[F_CFGHSACK]); - - cover(o_wb_ack && f_cfglswrite[F_CFGLSACK]); - cover(o_wb_ack && f_cfghsread[ F_CFGHSACK]); - cover(o_wb_ack && f_cfghswrite[F_CFGHSACK]); - end - end - - if (OPT_PIPE) - begin - always @(posedge i_clk) - cover(o_wb_ack && f_piperead[F_PIPEACK]); - end - // - // - // - always @(posedge i_clk) - cover((o_wb_ack)&&(!cfg_mode)); - always @(posedge i_clk) - cover((o_wb_ack)&&(!cfg_mode)&&(!$past(o_qspi_cs_n))); - always @(posedge i_clk) - // Cover a piped transaction - cover((o_wb_ack)&&(!cfg_mode)&&(!o_qspi_cs_n)); //! - always @(posedge i_clk) - cover((o_wb_ack)&&(cfg_mode)&&(cfg_speed)); - always @(posedge i_clk) - cover((o_wb_ack)&&(cfg_mode)&&(!cfg_speed)&&(cfg_dir)); - always @(posedge i_clk) - cover((o_wb_ack)&&(cfg_mode)&&(!cfg_speed)&&(!cfg_dir)); - end else begin - - always @(posedge i_clk) - cover(!maintenance); - - end endgenerate - -`endif -endmodule diff --git a/fw/rtl/external/wbsdram.v b/fw/rtl/external/wbsdram.v deleted file mode 100644 index fdd4bbc..0000000 --- a/fw/rtl/external/wbsdram.v +++ /dev/null @@ -1,1038 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Filename: wbsdram.v -// -// Project: ArrowZip, a demonstration of the Arrow MAX1000 FPGA board -// -// Purpose: Provide 32-bit wishbone access to the SDRAM memory on a MAX1000 -// board. Specifically, on each access, the controller will -// activate an appropriate bank of RAM (the SDRAM has four banks), and -// then issue the read/write command. In the case of walking off the -// bank, the controller will activate the next bank before you get to it. -// Upon concluding any wishbone access, all banks will be precharged and -// returned to idle. -// -// This particular implementation represents a second generation version -// because my first version was too complex. To speed things up, this -// version includes an extra wait state where the wishbone inputs are -// clocked into a flip flop before any action is taken on them. -// -// Creator: Dan Gisselquist, Ph.D. -// Gisselquist Technology, LLC -// -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2015-2019, Gisselquist Technology, LLC -// -// This program is free software (firmware): you can redistribute it and/or -// modify it under the terms of the GNU General Public License as published -// by the Free Software Foundation, either version 3 of the License, or (at -// your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program. (It's in the $(ROOT)/doc directory. Run make with no -// target there if the PDF file isn't present.) If not, see -// for a copy. -// -// License: GPL, v3, as defined and found on www.gnu.org, -// http://www.gnu.org/licenses/gpl.html -// -// -//////////////////////////////////////////////////////////////////////////////// -// -// -`default_nettype none -// -`define DMOD_GETINPUT 1'b0 -`define DMOD_PUTOUTPUT 1'b1 -`define RAM_OPERATIONAL 2'b00 -`define RAM_POWER_UP 2'b01 -`define RAM_SET_MODE 2'b10 -`define RAM_INITIAL_REFRESH 2'b11 -// -module wbsdram(i_clk, - i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, i_wb_sel, - o_wb_ack, o_wb_stall, o_wb_data, - o_ram_cs_n, o_ram_cke, o_ram_ras_n, o_ram_cas_n, o_ram_we_n, - o_ram_bs, o_ram_addr, - o_ram_dmod, i_ram_data, o_ram_data, o_ram_dqm, - o_debug); - parameter RDLY = 6; - localparam NCA=10, NRA=13, AW=(NCA+NRA+2)-1, DW=32; - localparam [NCA-2:0] COL_THRESHOLD = -16; - input wire i_clk; - // Wishbone - // inputs - input wire i_wb_cyc, i_wb_stb, i_wb_we; - input wire [(AW-1):0] i_wb_addr; - input wire [(DW-1):0] i_wb_data; - input wire [(DW/8-1):0] i_wb_sel; - // outputs - output wire o_wb_ack; - output reg o_wb_stall; - output wire [31:0] o_wb_data; - // SDRAM control - output wire o_ram_cke; - output reg o_ram_cs_n, - o_ram_ras_n, o_ram_cas_n, o_ram_we_n; - output reg [1:0] o_ram_bs; - output reg [12:0] o_ram_addr; - output reg o_ram_dmod; - input [15:0] i_ram_data; - output reg [15:0] o_ram_data; - output reg [1:0] o_ram_dqm; - output wire [(DW-1):0] o_debug; - - - // Calculate some metrics - - // - // First, do we *need* a refresh now --- i.e., must we break out of - // whatever we are doing to issue a refresh command? - // - // The step size here must be such that 8192 charges may be done in - // 64 ms. Thus for a clock of: - // ClkRate(MHz) (64ms/1000(ms/s)*ClkRate)/8192 - // 100 MHz 781 - // 96 MHz 750 - // 92 MHz 718 - // 88 MHz 687 - // 84 MHz 656 - // 80 MHz 625 - // - // However, since we do two refresh cycles everytime we need a refresh, - // this standard is close to overkill--but we'll use it anyway. At - // some later time we should address this, once we are entirely - // convinced that the memory is otherwise working without failure. Of - // course, at that time, it may no longer be a priority ... - // - reg need_refresh; - reg [9:0] refresh_clk; - wire refresh_cmd; - assign refresh_cmd = (!o_ram_cs_n)&&(!o_ram_ras_n)&&(!o_ram_cas_n)&&(o_ram_we_n); - initial refresh_clk = 0; - always @(posedge i_clk) - begin - if (refresh_cmd) - refresh_clk <= 10'd703; // Make suitable for 90 MHz clk - else if (|refresh_clk) - refresh_clk <= refresh_clk - 10'h1; - end - initial need_refresh = 1'b0; - always @(posedge i_clk) - need_refresh <= (refresh_clk == 10'h00)&&(!refresh_cmd); - - reg in_refresh; - reg [2:0] in_refresh_clk; - initial in_refresh_clk = 3'h0; - always @(posedge i_clk) - if (refresh_cmd) - in_refresh_clk <= 3'h6; - else if (|in_refresh_clk) - in_refresh_clk <= in_refresh_clk - 3'h1; - initial in_refresh = 0; - always @(posedge i_clk) - in_refresh <= (in_refresh_clk != 3'h0)||(refresh_cmd); -`ifdef FORMAL - always @(posedge i_clk) - if (in_refresh) - assert((refresh_cmd)||($past(in_refresh_clk) <= 3'h6)); - always @(posedge i_clk) - if (in_refresh) - assert(refresh_clk==10'd619+{{(7){1'b0}},in_refresh_clk}); -`endif - - - reg [2:0] bank_active [0:3]; - reg [(RDLY-1):0] r_barrell_ack; - reg r_pending; - reg r_we; - reg [(AW-1):0] r_addr; - reg [31:0] r_data; - reg [3:0] r_sel; - reg [(AW-NCA-2):0] bank_row [0:3]; - - - // - // Second, do we *need* a precharge now --- must be break out of - // whatever we are doing to issue a precharge command? - // - // Keep in mind, the number of clocks to wait has to be reduced by - // the amount of time it may take us to go into a precharge state. - // You may also notice that the precharge requirement is tighter - // than this one, so ... perhaps this isn't as required? - // - - reg [2:0] clocks_til_idle; - reg [1:0] m_state; - wire bus_cyc; - assign bus_cyc = ((i_wb_cyc)&&(i_wb_stb)&&(!o_wb_stall)); - reg nxt_dmod; - - // Pre-process pending operations - wire pending; - initial r_pending = 1'b0; - reg [(AW-1):0] fwd_addr; - initial r_addr = 0; - initial fwd_addr = { {(AW-(NCA)){1'b0}}, 1'b1, {(NCA-1){1'b0}} }; - always @(posedge i_clk) - begin - fwd_addr[NCA-2:0] <= 0; - if (bus_cyc) - begin - r_pending <= 1'b1; - r_we <= i_wb_we; - r_addr <= i_wb_addr; - r_data <= i_wb_data; - r_sel <= i_wb_sel; - fwd_addr[AW-1:NCA-1]<=i_wb_addr[(AW-1):(NCA-1)] + 1'b1; - end else if ((!o_ram_cs_n)&&(o_ram_ras_n)&&(!o_ram_cas_n)) - r_pending <= 1'b0; - else if (!i_wb_cyc) - r_pending <= 1'b0; - end - -`ifdef FORMAL - always @(*) - assert(fwd_addr[AW-1:NCA-1] == r_addr[(AW-1):(NCA-1)] + 1'b1); - always @(*) - assert(fwd_addr[NCA-3:0] == 0); -`endif - - wire [1:0] wb_bs, r_bs, fwd_bs; // Bank select - assign wb_bs = i_wb_addr[NCA:NCA-1]; - assign r_bs = r_addr[NCA:NCA-1]; - assign fwd_bs = fwd_addr[NCA:NCA-1]; - wire [NRA-1:0] wb_row, r_row, fwd_row; - assign wb_row = i_wb_addr[AW-1:NCA+1]; - assign r_row = r_addr[AW-1:NCA+1]; - assign fwd_row = fwd_addr[AW-1:NCA+1]; - - reg r_bank_valid; - initial r_bank_valid = 1'b0; - always @(posedge i_clk) - if (bus_cyc) - r_bank_valid <=((bank_active[wb_bs][2]) - &&(bank_row[wb_bs] == wb_row)); - else - r_bank_valid <= ((bank_active[r_bs][2]) - &&(bank_row[r_bs] == r_row)); - - reg fwd_bank_valid; - initial fwd_bank_valid = 0; - always @(posedge i_clk) - fwd_bank_valid <= ((bank_active[fwd_bs][2]) - &&(bank_row[fwd_bs] == fwd_row)); - - assign pending = (r_pending)&&(o_wb_stall); - - // - // - // Maintenance mode (i.e. startup) wires and logic - reg maintenance_mode; - reg m_ram_cs_n, m_ram_ras_n, m_ram_cas_n, m_ram_we_n, m_ram_dmod; - reg [(NRA-1):0] m_ram_addr; - // - // - // - - // Address MAP: - // 22-bits bits in, 23-bits out - // - // 22 1111 1111 1100 0000 0000 - // 10 9876 5432 1098 7654 3210 - // rr rrrr rrrr rrBB cccc cccc 0 - // 8765 4321 0 - // - initial r_barrell_ack = 0; - initial clocks_til_idle = 3'h0; - initial o_wb_stall = 1'b1; - initial o_ram_dmod = `DMOD_GETINPUT; - initial nxt_dmod = `DMOD_GETINPUT; - initial o_ram_cs_n = 1'b0; - initial o_ram_ras_n = 1'b1; - initial o_ram_cas_n = 1'b1; - initial o_ram_we_n = 1'b1; - initial o_ram_dqm = 2'b11; - assign o_ram_cke = 1'b1; - initial bank_active[0] = 3'b000; - initial bank_active[1] = 3'b000; - initial bank_active[2] = 3'b000; - initial bank_active[3] = 3'b000; - always @(posedge i_clk) - if (maintenance_mode) - begin - bank_active[0] <= 0; - bank_active[1] <= 0; - bank_active[2] <= 0; - bank_active[3] <= 0; - r_barrell_ack[(RDLY-1):0] <= 0; - o_wb_stall <= 1'b1; - // - o_ram_cs_n <= m_ram_cs_n; - o_ram_ras_n <= m_ram_ras_n; - o_ram_cas_n <= m_ram_cas_n; - o_ram_we_n <= m_ram_we_n; - o_ram_dmod <= m_ram_dmod; - o_ram_addr <= m_ram_addr; - o_ram_bs <= 2'b00; - nxt_dmod <= `DMOD_GETINPUT; - end else begin - o_wb_stall <= (r_pending)||(bus_cyc); - if (!i_wb_cyc) - r_barrell_ack <= 0; - else - r_barrell_ack <= r_barrell_ack >> 1; - nxt_dmod <= `DMOD_GETINPUT; - o_ram_dmod <= nxt_dmod; - - // - // We assume that, whatever state the bank is in, that it - // continues in that state and set up a series of shift - // registers to contain that information. If it will not - // continue in that state, all that therefore needs to be - // done is to set bank_active[?][2] below. - // - bank_active[0] <= { bank_active[0][2], bank_active[0][2:1] }; - bank_active[1] <= { bank_active[1][2], bank_active[1][2:1] }; - bank_active[2] <= { bank_active[2][2], bank_active[2][2:1] }; - bank_active[3] <= { bank_active[3][2], bank_active[3][2:1] }; - // - o_ram_cs_n <= (!i_wb_cyc); - // o_ram_cke <= 1'b1; - if (|clocks_til_idle[2:0]) - clocks_til_idle[2:0] <= clocks_til_idle[2:0] - 3'h1; - - // Default command is a - // NOOP if (i_wb_cyc) - // Device deselect if (!i_wb_cyc) - // o_ram_cs_n <= (!i_wb_cyc) above, NOOP - o_ram_ras_n <= 1'b1; - o_ram_cas_n <= 1'b1; - o_ram_we_n <= 1'b1; - - // o_ram_data <= r_data[15:0]; - - if (nxt_dmod) - ; - else - if ((!i_wb_cyc)||(need_refresh)) - begin // Issue a precharge all command (if any banks are open), - // otherwise an autorefresh command - if ((bank_active[0][2:1]==2'b10) - ||(bank_active[1][2:1]==2'b10) - ||(bank_active[2][2:1]==2'b10) - ||(bank_active[3][2:1]==2'b10) - ||(|clocks_til_idle[2:0])) - begin - // Do nothing this clock - // Can't precharge a bank immediately after - // activating it - end else if (bank_active[0][2] - ||(bank_active[1][2]) - ||(bank_active[2][2]) - ||(bank_active[3][2])) - begin // Close all active banks - o_ram_cs_n <= 1'b0; - o_ram_ras_n <= 1'b0; - o_ram_cas_n <= 1'b1; - o_ram_we_n <= 1'b0; - o_ram_addr[10] <= 1'b1; - bank_active[0][2] <= 1'b0; - bank_active[1][2] <= 1'b0; - bank_active[2][2] <= 1'b0; - bank_active[3][2] <= 1'b0; - end else if ((|bank_active[0]) - ||(|bank_active[1]) - ||(|bank_active[2]) - ||(|bank_active[3])) - // Can't precharge yet, the bus is still busy - begin end else if ((!in_refresh)&&((refresh_clk[9:8]==2'b00)||(need_refresh))) - begin // Send autorefresh command - o_ram_cs_n <= 1'b0; - o_ram_ras_n <= 1'b0; - o_ram_cas_n <= 1'b0; - o_ram_we_n <= 1'b1; - end // Else just send NOOP's, the default command - end else if (in_refresh) - begin - // NOOPS only here, until we are out of refresh - end else if ((pending)&&(!r_bank_valid)&&(bank_active[r_bs]==3'h0)) - begin // Need to activate the requested bank - o_ram_cs_n <= 1'b0; - o_ram_ras_n <= 1'b0; - o_ram_cas_n <= 1'b1; - o_ram_we_n <= 1'b1; - o_ram_addr <= r_row; - o_ram_bs <= r_bs; - // clocks_til_idle[2:0] <= 1; - bank_active[r_bs][2] <= 1'b1; - bank_row[r_bs] <= r_row; - // - end else if ((pending)&&(!r_bank_valid) - &&(bank_active[r_bs]==3'b111)) - begin // Need to close an active bank - o_ram_cs_n <= 1'b0; - o_ram_ras_n <= 1'b0; - o_ram_cas_n <= 1'b1; - o_ram_we_n <= 1'b0; - // o_ram_addr <= r_addr[(AW-1):(NCA+2)]; - o_ram_addr[10]<= 1'b0; - o_ram_bs <= r_bs; - // clocks_til_idle[2:0] <= 1; - bank_active[r_bs][2] <= 1'b0; - // bank_row[r_bs] <= r_row; - end else if ((pending)&&(!r_we) - &&(bank_active[r_bs][2]) - &&(r_bank_valid) - &&(clocks_til_idle[2:0] < 4)) - begin // Issue the read command - o_ram_cs_n <= 1'b0; - o_ram_ras_n <= 1'b1; - o_ram_cas_n <= 1'b0; - o_ram_we_n <= 1'b1; - o_ram_addr <= { 4'h0, r_addr[NCA-2:0], 1'b0 }; - o_ram_bs <= r_bs; - clocks_til_idle[2:0] <= 4; - - o_wb_stall <= 1'b0; - r_barrell_ack[(RDLY-1)] <= 1'b1; - end else if ((pending)&&(r_we) - &&(bank_active[r_bs][2]) - &&(r_bank_valid) - &&(clocks_til_idle[2:0] == 0)) - begin // Issue the write command - o_ram_cs_n <= 1'b0; - o_ram_ras_n <= 1'b1; - o_ram_cas_n <= 1'b0; - o_ram_we_n <= 1'b0; - o_ram_addr <= { 4'h0, r_addr[NCA-2:0], 1'b0 }; - o_ram_bs <= r_bs; - clocks_til_idle[2:0] <= 3'h1; - - o_wb_stall <= 1'b0; - r_barrell_ack[1] <= 1'b1; - // o_ram_data <= r_data[31:16]; - // - o_ram_dmod <= `DMOD_PUTOUTPUT; - nxt_dmod <= `DMOD_PUTOUTPUT; - end else if ((r_pending)&&(r_addr[(NCA-2):0] >= COL_THRESHOLD) - &&(!fwd_bank_valid)) - begin - // Do I need to close the next bank I'll need? - if (bank_active[fwd_bs][2:1]==2'b11) - begin // Need to close the bank first - o_ram_cs_n <= 1'b0; - o_ram_ras_n <= 1'b0; - o_ram_cas_n <= 1'b1; - o_ram_we_n <= 1'b0; - o_ram_addr[10] <= 1'b0; - o_ram_bs <= fwd_bs; - bank_active[fwd_bs][2] <= 1'b0; - end else if (bank_active[fwd_bs]==0) - begin - // Need to (pre-)activate the next bank - o_ram_cs_n <= 1'b0; - o_ram_ras_n <= 1'b0; - o_ram_cas_n <= 1'b1; - o_ram_we_n <= 1'b1; - o_ram_addr <= fwd_row; - o_ram_bs <= fwd_bs; - // clocks_til_idle[3:0] <= 1; - bank_active[fwd_bs] <= 3'h4; - bank_row[fwd_bs] <= fwd_row; - end - end - if (!i_wb_cyc) - r_barrell_ack <= 0; - end - - reg startup_hold; - reg [15:0] startup_idle; - initial startup_idle = 16'd20500; - initial startup_hold = 1'b1; - always @(posedge i_clk) - if (|startup_idle) - startup_idle <= startup_idle - 1'b1; - always @(posedge i_clk) - startup_hold <= |startup_idle; -`ifdef FORMAL - always @(*) - if (startup_hold) - assert(maintenance_mode); - always @(*) - if (|startup_idle) - assert(startup_hold); -`endif - - reg [3:0] maintenance_clocks; - reg maintenance_clocks_zero; - initial maintenance_mode = 1'b1; - initial maintenance_clocks = 4'hf; - initial maintenance_clocks_zero = 1'b0; - initial m_ram_addr = { 2'b00, 1'b0, 2'b00, 3'b010, 1'b0, 3'b001 }; - initial m_state = `RAM_POWER_UP; - initial m_ram_cs_n = 1'b1; - initial m_ram_ras_n = 1'b1; - initial m_ram_cas_n = 1'b1; - initial m_ram_we_n = 1'b1; - initial m_ram_dmod = `DMOD_GETINPUT; - always @(posedge i_clk) - begin - if (!maintenance_clocks_zero) - begin - maintenance_clocks <= maintenance_clocks - 4'h1; - maintenance_clocks_zero <= (maintenance_clocks == 4'h1); - end - // The only time the RAM address matters is when we set - // the mode. At other times, addr[10] matters, but the rest - // is ignored. Hence ... we'll set it to a constant. - m_ram_addr <= { 2'b00, 1'b0, 2'b00, 3'b010, 1'b0, 3'b001 }; - if (m_state == `RAM_POWER_UP) - begin - // All signals must be held in NOOP state during powerup - // m_ram_cke <= 1'b1; - m_ram_cs_n <= 1'b1; - m_ram_ras_n <= 1'b1; - m_ram_cas_n <= 1'b1; - m_ram_we_n <= 1'b1; - m_ram_dmod <= `DMOD_GETINPUT; - if (!startup_hold) - begin - m_state <= `RAM_SET_MODE; - maintenance_clocks <= 4'h3; - maintenance_clocks_zero <= 1'b0; - // Precharge all cmd - m_ram_cs_n <= 1'b0; - m_ram_ras_n <= 1'b0; - m_ram_cas_n <= 1'b1; - m_ram_we_n <= 1'b0; - m_ram_addr[10] <= 1'b1; - end - end else if (m_state == `RAM_SET_MODE) - begin - // Wait - m_ram_cs_n <= 1'b1; - m_ram_cs_n <= 1'b1; - m_ram_ras_n <= 1'b1; - m_ram_cas_n <= 1'b1; - m_ram_we_n <= 1'b1; - m_ram_addr[10] <= 1'b1; - - if (maintenance_clocks_zero) - begin - // Set mode cycle - m_ram_cs_n <= 1'b0; - m_ram_ras_n <= 1'b0; - m_ram_cas_n <= 1'b0; - m_ram_we_n <= 1'b0; - m_ram_dmod <= `DMOD_GETINPUT; - m_ram_addr[10] <= 1'b0; - - m_state <= `RAM_INITIAL_REFRESH; - maintenance_clocks <= 4'hc; - maintenance_clocks_zero <= 1'b0; - end - end else if (m_state == `RAM_INITIAL_REFRESH) - begin - // Refresh command - if (maintenance_clocks > 4'ha) - // Wait two clocks first - m_ram_cs_n <= 1'b1; - else if (maintenance_clocks > 4'h2) - m_ram_cs_n <= 1'b0; - else - m_ram_cs_n <= 1'b1; - m_ram_ras_n <= 1'b0; - m_ram_cas_n <= 1'b0; - m_ram_we_n <= 1'b1; - m_ram_dmod <= `DMOD_GETINPUT; - // m_ram_addr <= { 3'b000, 1'b0, 2'b00, 3'b010, 1'b0, 3'b001 }; - if (maintenance_clocks_zero) - maintenance_mode <= 1'b0; - end - end - - always @(posedge i_clk) - if (nxt_dmod) - o_ram_data <= r_data[15:0]; - else - o_ram_data <= r_data[31:16]; - - always @(posedge i_clk) - if (maintenance_mode) - o_ram_dqm <= 2'b11; - else if (r_we) - begin - if (nxt_dmod) - o_ram_dqm <= ~r_sel[1:0]; - else - o_ram_dqm <= ~r_sel[3:2]; - end else - o_ram_dqm <= 2'b00; - -`ifdef VERILATOR - // While I hate to build something that works one way under Verilator - // and another way in practice, this really isn't that. The problem - // \/erilator is having is resolved in toplevel.v---one file that - // \/erilator doesn't implement. In toplevel.v, there's not only a - // single clocked latch but two taking place. Here, we replicate one - // of those. The second takes place (somehow) within the sdramsim.cpp - // file. - reg [15:0] ram_data, last_ram_data; - always @(posedge i_clk) - ram_data <= i_ram_data; - always @(posedge i_clk) - last_ram_data <= ram_data; - assign o_wb_data = { last_ram_data, ram_data }; -`else - reg [15:0] last_ram_data; - always @(posedge i_clk) - last_ram_data <= i_ram_data; - assign o_wb_data = { last_ram_data, i_ram_data }; -`endif - assign o_wb_ack = r_barrell_ack[0]; - - // - // The following outputs are not necessary for the functionality of - // the SDRAM, but they can be used to feed an external "scope" to - // get an idea of what the internals of this SDRAM are doing. - // - // Just be aware of the r_we: it is set based upon the currently pending - // transaction, or (if none is pending) based upon the last transaction. - // If you want to capture the first value "written" to the device, - // you'll need to write a nothing value to the device to set r_we. - // The first value "written" to the device can be caught in the next - // interaction after that. - // - reg trigger; - always @(posedge i_clk) - trigger <= ((o_wb_data[15:0]==o_wb_data[31:16]) - &&(o_wb_ack)&&(!i_wb_we)); - - - assign o_debug = { i_wb_cyc, i_wb_stb, i_wb_we, o_wb_ack, o_wb_stall, // 5 - o_ram_cs_n, o_ram_ras_n, o_ram_cas_n, o_ram_we_n, o_ram_bs,//6 - o_ram_dmod, r_pending, // 2 - trigger, // 1 - o_ram_addr[9:0], // 10 more - (r_we) ? { o_ram_data[7:0] } // 8 values - : { o_wb_data[23:20], o_wb_data[3:0] } - // i_ram_data[7:0] - }; - - // Make Verilator happy - // verilator lint_off UNUSED - wire [NCA-1:0] unused; - assign unused = { fwd_addr[NCA-1:0] }; - // verilator lint_on UNUSED -`ifdef FORMAL - localparam REFRESH_CLOCKS = 6; - localparam ACTIVATE_CLOCKS = 6; - - // This device is 23MB, assert such - always @(*) - assert(AW == 21); - always @(*) - assert(NRA+NCA+2 == AW+1); - - wire [(5-1):0] f_nreqs, f_nacks, f_outstanding; - reg f_past_valid; - wire f_reset; - - always @(*) - if (o_ram_dmod) - assume(i_ram_data == o_ram_data); - - initial f_past_valid = 1'b0; - always @(posedge i_clk) - f_past_valid <= 1'b1; - - assign f_reset = !f_past_valid; - - always @(*) - if (o_ram_dmod) - assert(i_ram_data == o_ram_data); - - // Properties - // 1. Wishbone - fwb_slave #( .AW(AW), .DW(DW), - .F_MAX_STALL(ACTIVATE_CLOCKS + REFRESH_CLOCKS - + ACTIVATE_CLOCKS + RDLY - +ACTIVATE_CLOCKS), - .F_MAX_ACK_DELAY(REFRESH_CLOCKS - + ACTIVATE_CLOCKS - + ACTIVATE_CLOCKS - + ACTIVATE_CLOCKS+RDLY), - .F_LGDEPTH(5)) - fwb(i_clk, f_reset, - i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, - i_wb_data, i_wb_sel, - o_wb_ack, o_wb_stall, o_wb_data, 1'b0, - f_nreqs, f_nacks, f_outstanding); - - // 2. Proper startup ... - // 3. Operation - // 4. Refresh - // 4. SDRAM request == WB request - // - - // Once we leave maintenance mode (i.e. startup sequence), we *cannot* - // go back into it. - always @(posedge i_clk) - if ((f_past_valid)&&(!$past(maintenance_mode))) - assert(!maintenance_mode); - - // On the very first clock, we must always start up in maintenance mode - always @(posedge i_clk) - if (!f_past_valid) - assert(maintenance_mode); - - // Just to make things simpler, assume no accesses to the core during - // maintenance mode. Such accesses might violate our minimum - // acknowledgement time criteria for the wishbone above - always @(posedge i_clk) - if ((f_past_valid)&&(maintenance_mode)) - assume(!i_wb_stb); - - // Likewise, assert that there are *NO* outstanding transactions in - // this maintenance mode - always @(posedge i_clk) - if ((f_past_valid)&&(maintenance_mode)) - assert(f_outstanding == 0); - - // ... and that while we are in maintenance mode, any incoming request - // is stalled. This guarantees that our assumptions above are kept - // valid. - always @(posedge i_clk) - if ((f_past_valid)&&(maintenance_mode)) - assume(o_wb_stall); - - // If there are no attempts to access memory while in maintenance - // mode, then there should never be any pending operations upon - // completion of maintenance mode - always @(posedge i_clk) - if ((f_past_valid)&&(maintenance_mode)) - assert(!r_pending); - - wire [(2+AW+DW+DW/8-1):0] f_pending, f_request; - assign f_pending = { r_pending, r_we, r_addr, r_data, r_sel }; - assign f_request = { i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, i_wb_sel }; - - always @(posedge i_clk) - if ((f_past_valid)&&($past(r_pending))&&($past(i_wb_cyc)) - &&(($past(o_ram_cs_n)) - ||(!$past(o_ram_ras_n)) - ||($past(o_ram_cas_n))) ) - assert($stable(f_pending)); - - wire [4:0] f_cmd; - assign f_cmd = { o_ram_addr[10], - o_ram_cs_n, o_ram_ras_n, o_ram_cas_n, o_ram_we_n }; - -`define F_MODE_SET 5'b?0000 -`define F_BANK_PRECHARGE 5'b00010 -`define F_PRECHARGE_ALL 5'b10010 -`define F_BANK_ACTIVATE 5'b?0011 -`define F_WRITE 5'b00100 -`define F_READ 5'b00101 -`define F_REFRESH 5'b?0001 -`define F_NOOP 5'b?0111 - -`define F_BANK_ACTIVATE_S 4'b0011 -`define F_REFRESH_S 4'b0001 -`define F_NOOP_S 4'b0111 - - reg [(AW-1):0] f_next_addr; - always @(*) - begin - f_next_addr = 0; - f_next_addr[(AW-1):NCA-1] = r_addr[(AW-1):NCA-1] + 1'b1; - end - - wire [NRA-1:0] f_next_row, f_this_row; - wire [1:0] f_next_bank, f_this_bank; - assign f_next_row = f_next_addr[(AW-1):(NCA+1)]; - assign f_next_bank = f_next_addr[NCA:NCA-1]; - assign f_this_bank = r_bs; - assign f_this_row = r_row; - - always @(*) - if (o_ram_cs_n==1'b0) casez(f_cmd) - `F_MODE_SET: begin end - `F_BANK_PRECHARGE: begin end - `F_PRECHARGE_ALL: begin end - `F_BANK_ACTIVATE: begin end - `F_WRITE: begin end - `F_READ: begin end - `F_REFRESH: begin end - default: assert(f_cmd[3:0] == `F_NOOP_S); - endcase - - always @(posedge i_clk) - if ((f_past_valid)&&(!maintenance_mode)) - casez(f_cmd) - `F_BANK_ACTIVATE: begin - // Can only activate de-activated banks - assert(bank_active[o_ram_bs][1:0] == 0); - // Need to activate the right bank - if (o_ram_bs == $past(f_this_bank)) - assert($past(f_this_row)==o_ram_addr); - else if (o_ram_bs != 0) - begin - assert(o_ram_bs == $past(f_next_bank)); - assert($past(f_this_row)==o_ram_addr); - end else begin - assert(o_ram_bs == $past(f_next_bank)); - assert($past(f_next_row)==o_ram_addr); - end end - `F_BANK_PRECHARGE: begin - // Can only precharge (de-active) a fully active bank - assert(bank_active[o_ram_bs] == 3'b011); - end - `F_PRECHARGE_ALL: begin - // If pre-charging all, one of the banks must be active and in - // need of a pre-charge - assert( - (bank_active[0] == 3'b011) - ||(bank_active[1] == 3'b011) - ||(bank_active[2] == 3'b011) - ||(bank_active[3] == 3'b011) ); - end - `F_WRITE: begin - assert($past(r_we)); - assert(bank_active[o_ram_bs] == 3'b111); - assert(bank_row[o_ram_bs] == $past(f_this_row)); - assert(o_ram_bs == $past(f_this_bank)); - assert(o_ram_addr[0] == 1'b0); - end - `F_READ: begin - assert(!$past(r_we)); - assert(bank_active[o_ram_bs] == 3'b111); - assert(bank_row[o_ram_bs] == $past(f_this_row)); - assert(o_ram_bs == $past(f_this_bank)); - assert(o_ram_addr[0] == 1'b0); - end - `F_REFRESH: begin - // When giving a reset command, *all* banks must be inactive - assert( (bank_active[0] == 3'h0) - &&(bank_active[1] == 3'h0) - &&(bank_active[2] == 3'h0) - &&(bank_active[3] == 3'h0) ); - end - default: assert((o_ram_cs_n)||(f_cmd[3:0] == `F_NOOP_S)); - endcase - - integer f_k; - always @(posedge i_clk) - if ((f_past_valid)&&(!$past(maintenance_mode))) - begin - for(f_k=0; f_k<4; f_k=f_k+1) - if (((f_cmd[3:0] != `F_BANK_ACTIVATE_S)) - ||(o_ram_bs != f_k[1:0])) - assert($stable(bank_row[f_k[1:0]])); - end - - always @(posedge i_clk) - if ((f_past_valid)&&(!$past(maintenance_mode)) - &&($past(f_cmd) != `F_READ) - &&($past(f_cmd) != `F_WRITE) ) - begin - if (($past(r_pending))&&($past(i_wb_cyc))) - assert($stable(f_pending)); - end - - always @(posedge i_clk) - if ((f_past_valid)&&(!maintenance_mode)) - if ((r_pending)&&(f_cmd != `F_READ)&&(f_cmd != `F_WRITE)) - assert(o_wb_stall); - - always @(posedge i_clk) - if ((f_past_valid)&&(!$past(maintenance_mode))) - casez($past(f_cmd)) - `F_BANK_ACTIVATE: begin - assert(bank_active[$past(o_ram_bs)] == 3'b110); - assert(bank_row[$past(o_ram_bs)] == $past(o_ram_addr)); - end - `F_BANK_PRECHARGE: begin - assert(bank_active[$past(o_ram_bs)] == 3'b001); - end - `F_PRECHARGE_ALL: begin - assert(bank_active[0][2] == 1'b0); - assert(bank_active[1][2] == 1'b0); - assert(bank_active[2][2] == 1'b0); - assert(bank_active[3][2] == 1'b0); - end - // `F_WRITE: - // `F_READ: - `F_REFRESH: begin - assert(r_barrell_ack == 0); - end - default: begin end - endcase - - always @(posedge i_clk) - if ((f_past_valid)&&(!$past(maintenance_mode))) - begin - assert(bank_active[0][1:0] == $past(bank_active[0][2:1])); - assert(bank_active[1][1:0] == $past(bank_active[1][2:1])); - assert(bank_active[2][1:0] == $past(bank_active[2][2:1])); - assert(bank_active[3][1:0] == $past(bank_active[3][2:1])); - end - - always @(*) -`ifdef VERIFIC - if (in_refresh) -`else - if ((in_refresh)||(maintenance_mode)) -`endif - begin - assert(bank_active[0] == 0); - assert(bank_active[1] == 0); - assert(bank_active[2] == 0); - assert(bank_active[3] == 0); - end - - always @(posedge i_clk) - if ((f_past_valid)&&($past(o_wb_ack))) - assert(!o_wb_ack); - - reg [3:0] f_acks_pending; - always @(*) - begin - f_acks_pending = 0; - for(f_k=0; f_k + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Single Compressed Image + Internal Configuration + + + + + + Read only,Read only,Hidden,Read only,Hidden + $${FILENAME}_onchip_flash_0 + + ../sw/bootloader/build/SummerLoader64.hex + ../sw/bootloader/build/SummerLoader64.hex + + + + + + + diff --git a/fw/rtl/memory/embedded_flash.v b/fw/rtl/memory/embedded_flash.v new file mode 100644 index 0000000..97982a5 --- /dev/null +++ b/fw/rtl/memory/embedded_flash.v @@ -0,0 +1,56 @@ +module memory_embedded_flash ( + input i_clk, + input i_reset, + + input i_request, + output reg o_busy, + output o_ack, + input [18:0] i_address, + output reg [31:0] o_data +); + + localparam [18:0] ONCHIP_FLASH_END = 19'h059FF; + + wire w_onchip_flash_in_address_range = i_address <= ONCHIP_FLASH_END; + + reg r_dummy_ack; + + always @(posedge i_clk) begin + r_dummy_ack <= i_request && !w_onchip_flash_in_address_range; + end + + reg r_onchip_flash_request; + wire w_onchip_flash_busy; + wire w_onchip_flash_ack; + wire [31:0] w_onchip_flash_o_data; + + assign o_ack = r_dummy_ack || w_onchip_flash_ack; + + always @(*) begin + r_onchip_flash_request = 1'b0; + o_busy = 1'b0; + o_data = 32'h0000_0000; + if (w_onchip_flash_in_address_range) begin + r_onchip_flash_request = i_request; + o_busy = w_onchip_flash_busy; + o_data = { + w_onchip_flash_o_data[7:0], + w_onchip_flash_o_data[15:8], + w_onchip_flash_o_data[23:16], + w_onchip_flash_o_data[31:24], + }; + end + end + + onchip_flash onchip_flash_inst ( + .clock(i_clk), + .reset_n(~i_reset), + .avmm_data_addr(i_address), + .avmm_data_read(r_onchip_flash_request), + .avmm_data_readdata(w_onchip_flash_o_data), + .avmm_data_waitrequest(w_onchip_flash_busy), + .avmm_data_readdatavalid(w_onchip_flash_ack), + .avmm_data_burstcount(2'd1) + ); + +endmodule diff --git a/fw/rtl/memory/sdram.v b/fw/rtl/memory/sdram.v new file mode 100644 index 0000000..6728637 --- /dev/null +++ b/fw/rtl/memory/sdram.v @@ -0,0 +1,371 @@ +module memory_sdram ( + input i_clk, + input i_reset, + + output o_sdram_cs, + output o_sdram_ras, + output o_sdram_cas, + output o_sdram_we, + output [1:0] o_sdram_ba, + output [12:0] o_sdram_a, + inout [15:0] io_sdram_dq, + + input i_request, + input i_write, + output o_busy, + output reg o_ack, + input [24:0] i_address, + output reg [31:0] o_data, + input [31:0] i_data +); + + // SDRAM timings (in nanoseconds) + + parameter real CLK_FREQ = 90_000_000.0; + + parameter [2:0] CAS_LATENCY = 3'd2; + + parameter real T_INIT = 100_000.0; + parameter real T_RC = 60.0; + parameter real T_RP = 15.0; + parameter real T_RCD = 15.0; + parameter real T_RAS = 37.0; + parameter real T_WR = T_RAS - T_RCD; + parameter real T_MRD = 14.0; + parameter real T_REF = 7_800.0; + + localparam real T_CLK = (1.0 / CLK_FREQ) * 1_000_000_000.0; + localparam int C_INIT = int'((T_INIT + T_CLK - 1) / T_CLK); + localparam int C_RC = int'((T_RC + T_CLK - 1) / T_CLK); + localparam int C_RP = int'((T_RP + T_CLK - 1) / T_CLK); + localparam int C_RCD = int'((T_RCD + T_CLK - 1) / T_CLK); + localparam int C_RAS = int'((T_RAS + T_CLK - 1) / T_CLK); + localparam int C_WR = int'((T_WR + T_CLK - 1) / T_CLK); + localparam int C_MRD = int'((T_MRD + T_CLK - 1) / T_CLK); + localparam int C_REF = int'((T_REF + T_CLK - 1) / T_CLK); + + localparam INIT_PRECHARGE = C_INIT; + localparam INIT_REFRESH_1 = C_INIT + C_RP; + localparam INIT_REFRESH_2 = C_INIT + C_RP + C_RC; + localparam INIT_MODE_REG = C_INIT + C_RP + (2 * C_RC); + localparam INIT_DONE = C_INIT + C_RP + (2 * C_RC) + C_MRD; + + + // SDRAM commands (CS, RAS, CAS, WE) and mode register + + localparam [3:0] CMD_DESL = 4'b1111; + localparam [3:0] CMD_NOP = 4'b0111; + localparam [3:0] CMD_READ = 4'b0101; + localparam [3:0] CMD_WRITE = 4'b0100; + localparam [3:0] CMD_ACT = 4'b0011; + localparam [3:0] CMD_PRE = 4'b0010; + localparam [3:0] CMD_REF = 4'b0001; + localparam [3:0] CMD_MRS = 4'b0000; + + localparam MODE_REGISTER = {2'b00, 1'b0, 1'b0, 2'b00, CAS_LATENCY, 1'b0, 3'b000}; + + + // Command signal decoder + + reg [3:0] r_sdram_cmd; + + assign {o_sdram_cs, o_sdram_ras, o_sdram_cas, o_sdram_we} = r_sdram_cmd; + + + // Address signal decoder + + reg r_sdram_precharge; + reg [1:0] r_sdram_bank; + reg [12:0] r_sdram_row; + reg [9:0] r_sdram_column; + reg [14:0] r_active_bank_row; + + always @(*) begin + case (r_sdram_cmd) + CMD_READ, CMD_WRITE: o_sdram_a = {2'b00, r_sdram_precharge, r_sdram_column}; + CMD_ACT: o_sdram_a = r_sdram_row; + CMD_PRE: o_sdram_a = {2'b00, r_sdram_precharge, 10'b0000000000}; + CMD_MRS: o_sdram_a = MODE_REGISTER; + default: o_sdram_a = 13'b0000000000000; + endcase + end + + always @(*) begin + case (r_sdram_cmd) + CMD_READ, CMD_WRITE, CMD_ACT, CMD_PRE: o_sdram_ba = r_sdram_bank; + default: o_sdram_ba = 2'b00; + endcase + end + + always @(posedge i_clk) begin + if (i_request && !o_busy) begin + {r_sdram_bank, r_sdram_row, r_sdram_column} <= i_address; + end + if (r_sdram_cmd == CMD_ACT) begin + r_active_bank_row <= {r_sdram_bank, r_sdram_row}; + end + if (r_sdram_cmd == CMD_READ || r_sdram_cmd == CMD_WRITE) begin + {r_sdram_bank, r_sdram_row, r_sdram_column} <= {r_sdram_bank, r_sdram_row, r_sdram_column} + 1'b1; + end + end + + wire w_next_address_in_another_row = (&r_sdram_column); + wire w_request_in_another_row = i_address[24:10] != r_active_bank_row; + + + // Data signal decoder + + reg [31:0] r_sdram_data; + reg r_current_write_word; + + always @(*) begin + io_sdram_dq = 16'hZZZZ; + if (r_sdram_cmd == CMD_WRITE) begin + io_sdram_dq = r_current_write_word ? r_sdram_data[15:0] : r_sdram_data[31:16]; + end + end + + always @(posedge i_clk) begin + if (i_reset) begin + r_current_write_word <= 1'b0; + end else if (r_sdram_cmd == CMD_WRITE) begin + r_current_write_word <= ~r_current_write_word; + end + end + + + // Read latency timing + + reg [(CAS_LATENCY - 1):0] r_read_latency; + reg r_current_read_word; + + always @(posedge i_clk) begin + o_ack <= 1'b0; + if (i_reset) begin + r_read_latency <= {CAS_LATENCY{1'b0}}; + r_current_read_word <= 1'b0; + end else begin + r_read_latency <= {r_read_latency[(CAS_LATENCY - 2):0], r_sdram_cmd == CMD_READ}; + if (r_read_latency[CAS_LATENCY - 1]) begin + o_data <= {o_data[15:0], io_sdram_dq}; + if (r_current_read_word) o_ack <= 1'b1; + r_current_read_word <= ~r_current_read_word; + end + end + end + + wire w_read_pending = |r_read_latency; + + + // Init timing and logic + + reg [15:0] r_init_counter; + + always @(posedge i_clk) begin + if (i_reset) begin + r_init_counter <= 1'd0; + end else if (r_init_counter < INIT_DONE) begin + r_init_counter <= r_init_counter + 1'd1; + end + end + + wire w_init_hold = r_init_counter <= C_INIT - 1; + wire w_init_precharge = r_init_counter == INIT_PRECHARGE; + wire w_init_refresh_1 = r_init_counter == INIT_REFRESH_1; + wire w_init_refresh_2 = r_init_counter == INIT_REFRESH_2; + wire w_init_mode_reg = r_init_counter == INIT_MODE_REG; + wire w_init_done = r_init_counter == INIT_DONE; + + + // SDRAM controller FSM + + localparam [2:0] STATE_INIT = 3'd0; + localparam [2:0] STATE_IDLE = 3'd1; + localparam [2:0] STATE_ACTIVATING = 3'd2; + localparam [2:0] STATE_ACTIVE = 3'd3; + localparam [2:0] STATE_PRECHARGING = 3'd4; + localparam [2:0] STATE_REFRESHING = 3'd5; + + reg [9:0] r_refresh_counter; + reg [4:0] r_rcd_ras_rc_counter; + reg [1:0] r_wr_counter; + reg [2:0] r_rp_counter; + + wire w_refresh_pending = r_refresh_counter >= (C_REF - 1'd1); + wire w_rcd_timing_met = r_rcd_ras_rc_counter >= (C_RCD - 1'd1); + wire w_ras_timing_met = r_rcd_ras_rc_counter >= (C_RAS - 1'd1); + wire w_rc_timing_met = r_rcd_ras_rc_counter >= (C_RC - 1'd1); + wire w_wr_timing_met = r_wr_counter >= (C_WR - 1'd1); + wire w_rp_timing_met = r_rp_counter >= (C_RP - 1'd1); + + reg [2:0] r_state; + reg r_busy; + reg r_cross_row_request; + reg r_request_pending; + reg r_write_pending; + reg r_current_word; + reg r_wr_wait; + + assign o_busy = i_request && ( + w_refresh_pending || + r_busy || + r_request_pending || + r_cross_row_request || + w_read_pending || + r_sdram_cmd == CMD_READ || + r_sdram_cmd == CMD_WRITE + ); + + always @(posedge i_clk) begin + if (i_reset || w_init_hold) begin + r_sdram_cmd <= CMD_DESL; + r_state <= STATE_INIT; + r_busy <= 1'b1; + r_cross_row_request <= 1'b0; + r_wr_wait <= 1'b0; + end else begin + r_sdram_cmd <= CMD_NOP; + r_sdram_precharge <= 1'b0; + + if (r_refresh_counter < (C_REF - 1)) r_refresh_counter <= r_refresh_counter + 1'd1; + if (r_rcd_ras_rc_counter < (C_RC - 1)) r_rcd_ras_rc_counter <= r_rcd_ras_rc_counter + 1'd1; + if (r_wr_counter < (C_WR - 1)) r_wr_counter <= r_wr_counter + 1'd1; + if (r_rp_counter < (C_RP - 1)) r_rp_counter <= r_rp_counter + 1'd1; + + case (r_state) + STATE_INIT: begin + if (w_init_precharge) begin + r_sdram_cmd <= CMD_PRE; + r_sdram_precharge <= 1'b1; + end else if (w_init_refresh_1 || w_init_refresh_2) begin + r_sdram_cmd <= CMD_REF; + r_refresh_counter <= 1'd0; + end else if (w_init_mode_reg) begin + r_sdram_cmd <= CMD_MRS; + end else if (w_init_done) begin + r_state <= STATE_IDLE; + r_busy <= 1'b0; + end + end + + STATE_IDLE: begin + if (w_refresh_pending) begin + r_sdram_cmd <= CMD_REF; + r_refresh_counter <= 1'd0; + r_rcd_ras_rc_counter <= 1'd0; + r_state <= STATE_REFRESHING; + r_busy <= 1'b1; + end else if (r_request_pending || r_cross_row_request) begin + r_sdram_cmd <= CMD_ACT; + r_rcd_ras_rc_counter <= 1'd0; + r_state <= STATE_ACTIVATING; + r_busy <= 1'b1; + end else if (i_request && !o_busy) begin + r_sdram_cmd <= CMD_ACT; + r_sdram_data <= i_data; + r_rcd_ras_rc_counter <= 1'd0; + r_state <= STATE_ACTIVATING; + r_request_pending <= 1'b1; + r_write_pending <= i_write; + r_current_word <= 1'b0; + r_busy <= 1'b1; + end + end + + STATE_ACTIVATING: begin + if (w_rcd_timing_met) begin + r_sdram_cmd <= r_write_pending ? CMD_WRITE : CMD_READ; + if (r_write_pending) r_wr_counter <= 1'd0; + if (r_cross_row_request) begin + r_cross_row_request <= 1'b0; + r_busy <= 1'b0; + end else if (r_request_pending) begin + r_current_word <= 1'b1; + end + r_state <= STATE_ACTIVE; + end + end + + STATE_ACTIVE: begin + if (r_wr_wait) begin + if (w_wr_timing_met) begin + r_rp_counter <= 1'd0; + r_state <= STATE_PRECHARGING; + r_wr_wait <= 1'b0; + end + end else if (r_request_pending && !(r_write_pending && w_read_pending)) begin + r_sdram_cmd <= r_write_pending ? CMD_WRITE : CMD_READ; + if (r_write_pending) r_wr_counter <= 1'd0; + r_current_word <= 1'b1; + if (r_current_word) begin + r_busy <= 1'b0; + r_request_pending <= 1'b0; + end + end else if (w_refresh_pending) begin + if (w_ras_timing_met && w_wr_timing_met) begin + r_sdram_cmd <= CMD_PRE; + r_sdram_precharge <= 1'b1; + r_rp_counter <= 1'd0; + r_state <= STATE_PRECHARGING; + end + end else if (i_request && !o_busy) begin + r_sdram_data <= i_data; + r_busy <= 1'b1; + r_write_pending <= i_write; + + if (w_request_in_another_row || (i_write && w_read_pending)) begin + r_request_pending <= 1'b1; + r_current_word <= 1'b0; + if (!(i_write && w_read_pending)) begin + if (!w_wr_timing_met) begin + r_wr_wait <= 1'b1; + end else begin + r_sdram_cmd <= CMD_PRE; + r_sdram_precharge <= 1'b1; + r_rp_counter <= 1'd0; + r_state <= STATE_PRECHARGING; + end + end + end else begin + r_sdram_cmd <= i_write ? CMD_WRITE : CMD_READ; + if (i_write) r_wr_counter <= 1'd0; + r_current_word <= 1'b1; + if (w_next_address_in_another_row) begin + r_sdram_precharge <= 1'b1; + r_cross_row_request <= 1'b1; + if (!i_write) begin + r_rp_counter <= 1'd0; + r_state <= STATE_PRECHARGING; + end else begin + r_wr_wait <= 1'b1; + end + end else begin + r_request_pending <= 1'b1; + end + end + end + end + + STATE_PRECHARGING: begin + if (w_rc_timing_met && w_rp_timing_met) begin + r_state <= STATE_IDLE; + r_busy <= r_request_pending || r_cross_row_request; + end + end + + STATE_REFRESHING: begin + if (w_rc_timing_met) begin + r_state <= STATE_IDLE; + r_busy <= 1'b0; + end + end + + default: begin + r_state <= STATE_IDLE; + r_busy <= 1'b0; + end + endcase + end + end + +endmodule diff --git a/fw/rtl/n64/bank_decoder.v b/fw/rtl/n64/bank_decoder.v new file mode 100644 index 0000000..0d96966 --- /dev/null +++ b/fw/rtl/n64/bank_decoder.v @@ -0,0 +1,42 @@ +module n64_bank_decoder ( + input [31:0] i_address, + output reg [25:0] o_translated_address, + output reg [3:0] o_bank, + output reg o_bank_prefetch +); + + localparam [3:0] BANK_INVALID = 4'd0; + localparam [3:0] BANK_ROM = 4'd1; + localparam [3:0] BANK_CART = 4'd2; + localparam [3:0] BANK_EEPROM = 4'd3; + + localparam [31:0] ROM_BASE = 32'h1000_0000; + localparam [31:0] ROM_END = 32'h13FF_FFFF; + + localparam [31:0] CART_BASE = 32'h1E00_0000; + localparam [31:0] CART_END = 32'h1EFF_FFFF; + + localparam [31:0] EEPROM_BASE = 32'h1D00_0000; + localparam [31:0] EEPROM_END = 32'h1D00_07FF; + + always @(*) begin + o_bank = BANK_INVALID; + o_bank_prefetch = 1'b0; + o_translated_address = i_address[25:0]; + if (i_address >= ROM_BASE && i_address <= ROM_END) begin + o_translated_address = i_address - ROM_BASE; + o_bank = BANK_ROM; + o_bank_prefetch = 1'b1; + end + if (i_address >= CART_BASE && i_address <= CART_END) begin + o_translated_address = i_address - CART_BASE; + o_bank = BANK_CART; + end + if (i_address >= EEPROM_BASE && i_address <= EEPROM_END) begin + o_translated_address = i_address - EEPROM_BASE; + o_bank = BANK_EEPROM; + o_bank_prefetch = 1'b1; + end + end + +endmodule diff --git a/fw/rtl/n64/pi.v b/fw/rtl/n64/pi.v new file mode 100644 index 0000000..d04175e --- /dev/null +++ b/fw/rtl/n64/pi.v @@ -0,0 +1,189 @@ +module n64_pi ( + input i_clk, + input i_reset, + + input i_n64_reset, + input i_n64_pi_alel, + input i_n64_pi_aleh, + input i_n64_pi_read, + input i_n64_pi_write, + inout [15:0] io_n64_pi_ad, + + output reg o_request, + output reg o_write, + input i_busy, + input i_ack, + output [3:0] o_bank, + output reg [25:0] o_address, + input [31:0] i_data, + output reg [31:0] o_data +); + + // Parameters + + parameter bit PREFETCH_DISABLE = 1'b0; + + + // Input synchronization + + reg r_reset_ff1, r_reset_ff2; + reg r_alel_ff1, r_alel_ff2; + reg r_aleh_ff1, r_aleh_ff2; + reg r_read_ff1, r_read_ff2; + reg r_write_ff1, r_write_ff2; + + always @(posedge i_clk) begin + {r_reset_ff2, r_reset_ff1} <= {r_reset_ff1, i_n64_reset}; + {r_alel_ff2, r_alel_ff1} <= {r_alel_ff1, i_n64_pi_alel}; + {r_aleh_ff2, r_aleh_ff1} <= {r_aleh_ff1, i_n64_pi_aleh}; + {r_read_ff2, r_read_ff1} <= {r_read_ff1, i_n64_pi_read}; + {r_write_ff2, r_write_ff1} <= {r_write_ff1, i_n64_pi_write}; + end + + + // PI event signals generator + + wire [1:0] w_pi_mode = {r_aleh_ff2, r_alel_ff2}; + reg [1:0] r_last_pi_mode; + reg r_last_read; + reg r_last_write; + + always @(posedge i_clk) begin + r_last_pi_mode <= w_pi_mode; + r_last_read <= r_read_ff2; + r_last_write <= r_write_ff2; + end + + localparam [1:0] PI_MODE_IDLE = 2'b10; + localparam [1:0] PI_MODE_HIGH = 2'b11; + localparam [1:0] PI_MODE_LOW = 2'b01; + localparam [1:0] PI_MODE_VALID = 2'b00; + + wire w_address_high_op = r_reset_ff2 && (r_last_pi_mode != PI_MODE_HIGH) && (w_pi_mode == PI_MODE_HIGH); + wire w_address_low_op = r_reset_ff2 && (r_last_pi_mode != PI_MODE_LOW) && (w_pi_mode == PI_MODE_LOW); + wire w_address_valid_op = r_reset_ff2 && (r_last_pi_mode != PI_MODE_VALID) && (w_pi_mode == PI_MODE_VALID); + wire w_read_op = r_reset_ff2 && (w_pi_mode == PI_MODE_VALID) && r_last_read && !r_read_ff2; + wire w_write_op = r_reset_ff2 && (w_pi_mode == PI_MODE_VALID) && r_last_write && !r_write_ff2; + + + // Bus address register + + reg [31:0] r_pi_address; + + always @(posedge i_clk) begin + if (w_address_high_op) r_pi_address[31:16] <= io_n64_pi_ad; + if (w_address_low_op) r_pi_address[15:0] <= {io_n64_pi_ad[15:1], 1'b0}; + end + + + // Bank decoder, address translator and prefetch signal + + wire [25:0] w_translated_address; + wire w_bank_prefetch; + wire w_prefetch = !PREFETCH_DISABLE && w_bank_prefetch; + + n64_bank_decoder n64_bank_decoder_inst ( + .i_address(r_pi_address), + .o_translated_address(w_translated_address), + .o_bank(o_bank), + .o_bank_prefetch(w_bank_prefetch) + ); + + + // Read/write current word logic + + reg r_word_counter; + + always @(posedge i_clk) begin + if (w_address_valid_op) r_word_counter <= 1'b0; + if (w_read_op || w_write_op) r_word_counter <= ~r_word_counter; + end + + + // N64 PI output data logic + + reg [31:0] r_pi_output_data; + + always @(*) begin + io_n64_pi_ad = 16'hZZZZ; + if (r_reset_ff2 && !r_read_ff2 && o_bank != 4'd0) begin + io_n64_pi_ad = r_word_counter ? r_pi_output_data[31:16] : r_pi_output_data[15:0]; + end + end + + + // Bus event signals generator + + wire w_bus_read_op = w_read_op && !r_word_counter; + wire w_bus_write_op = w_write_op && r_word_counter; + + + // Read buffer logic + + reg [31:0] r_pi_read_buffer; + + always @(posedge i_clk) begin + if (i_ack) begin + if (w_prefetch) r_pi_read_buffer <= i_data; + else r_pi_output_data <= i_data; + end + if (w_prefetch && w_bus_read_op) r_pi_output_data <= r_pi_read_buffer; + end + + + // Write data logic + + reg [15:0] r_pi_write_buffer; + + always @(posedge i_clk) begin + if (w_write_op) begin + if (!r_word_counter) begin + r_pi_write_buffer <= io_n64_pi_ad; + end else begin + o_data <= {r_pi_write_buffer, io_n64_pi_ad}; + end + end + end + + + // Bus request logic + + wire w_bus_request_op = !o_request && ((w_address_valid_op && w_prefetch) || w_bus_read_op || w_bus_write_op); + + always @(posedge i_clk) begin + if (i_reset) begin + o_request <= 1'b0; + o_write <= 1'b0; + end else begin + if (w_bus_request_op) begin + o_request <= 1'b1; + o_write <= w_bus_write_op; + end + if (o_request && !i_busy) begin + o_request <= 1'b0; + o_write <= 1'b0; + end + end + end + + + // Address increment logic + + reg r_first_transfer; + + wire w_address_increment_op = ( + (w_bus_read_op && (!r_first_transfer || w_prefetch)) || + (w_bus_write_op && !r_first_transfer) + ); + wire w_first_transfer_clear_op = w_bus_read_op || w_bus_write_op; + + always @(posedge i_clk) begin + if (w_address_valid_op) begin + o_address <= w_translated_address; + r_first_transfer <= 1'b1; + end + if (w_first_transfer_clear_op) r_first_transfer <= 1'b0; + if (w_address_increment_op) o_address[8:2] <= o_address[8:2] + 1'b1; + end + +endmodule diff --git a/fw/rtl/n64_si.v b/fw/rtl/n64/si.v similarity index 79% rename from fw/rtl/n64_si.v rename to fw/rtl/n64/si.v index 7177208..deec6ad 100644 --- a/fw/rtl/n64_si.v +++ b/fw/rtl/n64/si.v @@ -2,23 +2,41 @@ module n64_si ( input i_clk, input i_reset, - input i_si_clk, - input i_si_reset, - input i_si_dq, - output reg o_si_dq, + input i_n64_reset, + input i_n64_si_clk, + inout io_n64_si_dq, - input i_eeprom_select, - input i_read_rq, - input i_write_rq, + input i_request, + input i_write, + output o_busy, output reg o_ack, - input [31:0] i_address, + input [10:0] i_address, input [31:0] i_data, output [31:0] o_data, input i_eeprom_enable, - input i_eeprom_16k_enable + input i_eeprom_16k_mode ); + // Input synchronization + + reg r_reset_ff1, r_reset_ff2; + reg r_si_clk_ff1, r_si_clk_ff2; + reg r_si_dq_ff1, r_si_dq_ff2; + + always @(posedge i_clk) begin + {r_reset_ff2, r_reset_ff1} <= {r_reset_ff1, i_n64_reset}; + {r_si_clk_ff2, r_si_clk_ff1} <= {r_si_clk_ff1, i_n64_si_clk}; + {r_si_dq_ff2, r_si_dq_ff1} <= {r_si_dq_ff1, io_n64_si_dq}; + end + + reg r_si_dq_o; + + assign io_n64_si_dq = r_si_dq_o ? 1'bZ : 1'b0; + + + // SI commands + localparam CMD_EEPROM_STATUS = 8'h00; localparam CMD_EEPROM_READ = 8'h04; localparam CMD_EEPROM_WRITE = 8'h05; @@ -26,19 +44,23 @@ module n64_si ( localparam EEPROM_4K_ID = 8'h80; localparam EEPROM_16K_ID = 8'hC0; + + // Event signal generation + reg r_last_si_clk; reg r_last_si_dq; - wire w_si_clk_falling_edge = !i_reset && !i_si_reset && r_last_si_clk && !i_si_clk; - wire w_si_clk_rising_edge = !i_reset && !i_si_reset && !r_last_si_clk && i_si_clk; - wire w_si_dq_falling_edge = r_last_si_dq && !i_si_dq; - wire w_si_dq_rising_edge = !r_last_si_dq && i_si_dq; + wire w_si_clk_falling_edge = !i_reset && r_reset_ff2 && r_last_si_clk && !r_si_clk_ff2; + wire w_si_clk_rising_edge = !i_reset && r_reset_ff2 && !r_last_si_clk && r_si_clk_ff2; + wire w_si_dq_falling_edge = r_last_si_dq && !r_si_dq_ff2; + wire w_si_dq_rising_edge = !r_last_si_dq && r_si_dq_ff2; always @(posedge i_clk) begin - r_last_si_clk <= i_si_clk; - if (w_si_clk_rising_edge) r_last_si_dq <= i_si_dq; + r_last_si_clk <= r_si_clk_ff2; + if (w_si_clk_rising_edge) r_last_si_dq <= r_si_dq_ff2; end + // RX module reg r_rx_enabled; @@ -92,7 +114,7 @@ module n64_si ( wire w_cmd_op = r_rx_byte_ready && r_rx_byte_counter == 4'd0; always @(posedge i_clk) begin - if (i_reset || i_si_reset || w_rx_start || r_tx_finished) begin + if (i_reset || !r_reset_ff2 || w_rx_start || r_tx_finished) begin r_cmd_eeprom_status <= 1'b0; r_cmd_eeprom_read <= 1'b0; r_cmd_eeprom_write <= 1'b0; @@ -117,6 +139,7 @@ module n64_si ( if (w_eeprom_address_next_op) r_eeprom_address[2:0] <= r_eeprom_address[2:0] + 3'd1; end + // TX module reg [2:0] r_tx_sub_bit_counter; @@ -132,13 +155,13 @@ module n64_si ( always @(*) begin r_tx_data = 8'h00; - if (r_cmd_eeprom_status && r_tx_byte_counter == 4'd1) r_tx_data = i_eeprom_16k_enable ? EEPROM_16K_ID : EEPROM_4K_ID; + if (r_cmd_eeprom_status && r_tx_byte_counter == 4'd1) r_tx_data = i_eeprom_16k_mode ? EEPROM_16K_ID : EEPROM_4K_ID; if (r_cmd_eeprom_read) r_tx_data = w_eeprom_o_data; end always @(posedge i_clk) begin - if (i_reset || i_si_reset) begin - o_si_dq <= 1'b1; + if (i_reset || !r_reset_ff2) begin + r_si_dq_o <= 1'b1; r_rx_enabled <= 1'b1; r_tx_finished <= 1'b0; r_eeprom_read_rq <= 1'b0; @@ -161,12 +184,12 @@ module n64_si ( if (w_si_clk_falling_edge) begin r_tx_sub_bit_counter <= r_tx_sub_bit_counter + 3'd1; - if (r_tx_sub_bit_counter == 3'd0) o_si_dq <= 1'b0; + if (r_tx_sub_bit_counter == 3'd0) r_si_dq_o <= 1'b0; if ((w_tx_current_bit && r_tx_sub_bit_counter == 3'd2) || (!w_tx_current_bit && r_tx_sub_bit_counter == 3'd6) || (w_tx_stop_bit && r_tx_sub_bit_counter == 3'd4)) begin - o_si_dq <= 1'b1; + r_si_dq_o <= 1'b1; end if (&r_tx_sub_bit_counter) begin @@ -185,6 +208,7 @@ module n64_si ( end end + // Block RAM ram_n64_eeprom ram_n64_eeprom_inst ( @@ -197,14 +221,17 @@ module n64_si ( .address_b(i_address[10:2]), .data_b({i_data[7:0], i_data[15:8], i_data[23:16], i_data[31:24]}), - .wren_b(!i_reset && i_eeprom_select && i_write_rq), + .wren_b(!i_reset && i_request && i_write), .q_b({o_data[7:0], o_data[15:8], o_data[23:16], o_data[31:24]}) ); + // Bus logic always @(posedge i_clk) begin - o_ack <= !i_reset && i_eeprom_select && (i_read_rq || i_write_rq); + o_ack <= !i_reset && i_request; end + assign o_busy = 1'b0; + endmodule diff --git a/fw/rtl/n64_pi.v b/fw/rtl/n64_pi.v deleted file mode 100644 index 10f2009..0000000 --- a/fw/rtl/n64_pi.v +++ /dev/null @@ -1,95 +0,0 @@ -module n64_pi ( - input i_clk, - input i_reset, - - input [1:0] i_n64_pi_alel, - input [1:0] i_n64_pi_aleh, - input i_n64_pi_read, - input i_n64_pi_write, - input [15:0] i_n64_pi_ad, - output reg [15:0] o_n64_pi_ad, - output o_n64_pi_ad_mode, - - output reg o_read_rq, - output reg o_write_rq, - input i_ack, - output reg [31:0] o_address, - input [31:0] i_data, - output reg [31:0] o_data, - - input i_address_valid -); - - reg r_last_n64_pi_alel; - reg r_last_n64_pi_aleh; - reg r_last_n64_pi_read; - reg r_last_n64_pi_write; - - reg r_first_transfer; - reg r_word_select; - reg [31:0] r_data_i_buffer; - reg [15:0] r_word_buffer; - reg r_address_valid; - reg r_address_valid_buffer; - - wire w_aleh_valid = (&i_n64_pi_alel) && (&i_n64_pi_aleh); - wire w_alel_valid = (&i_n64_pi_alel) && (~|i_n64_pi_aleh); - wire w_address_op = r_last_n64_pi_alel && !i_n64_pi_alel[0] && !i_n64_pi_aleh[0]; - wire w_read_op = r_last_n64_pi_read && !i_n64_pi_read; - wire w_write_op = r_last_n64_pi_write && !i_n64_pi_write; - wire w_bus_read_op = w_read_op && r_word_select; - wire w_bus_write_op = w_write_op && !r_word_select; - - wire w_address_increment = w_bus_read_op || (w_bus_write_op && !r_first_transfer); - - assign o_n64_pi_ad_mode = !i_reset && !i_n64_pi_alel[0] && !i_n64_pi_aleh[0] && !i_n64_pi_read && !r_last_n64_pi_read && r_address_valid; - - always @(posedge i_clk) begin - r_last_n64_pi_alel <= i_n64_pi_alel[0]; - r_last_n64_pi_aleh <= i_n64_pi_aleh[0]; - r_last_n64_pi_read <= i_n64_pi_read; - r_last_n64_pi_write <= i_n64_pi_write; - - o_read_rq <= 1'b0; - o_write_rq <= 1'b0; - - if (!i_reset) begin - o_read_rq <= w_bus_read_op || w_address_op; - o_write_rq <= w_bus_write_op; - - if (w_aleh_valid) begin - o_address <= {i_n64_pi_ad, o_address[15:0]}; - end - if (w_alel_valid) begin - o_address <= {o_address[31:16], i_n64_pi_ad[15:1], 1'b0}; - end - if (w_address_op) begin - r_first_transfer <= 1'b1; - r_word_select <= 1'b1; - end - if (w_read_op || w_write_op) begin - r_word_select <= ~r_word_select; - o_address <= {o_address[31:10], (o_address[9:0] + {w_address_increment, 2'b00})}; - end - if (w_write_op && !r_word_select) begin - r_first_transfer <= 1'b0; - end - if (w_read_op) begin - {o_n64_pi_ad, r_word_buffer} <= r_word_select ? r_data_i_buffer : {r_word_buffer, 16'hXXXX}; - end - if (w_write_op) begin - o_data <= {o_data[15:0], i_n64_pi_ad}; - end - if (w_bus_read_op) begin - r_address_valid <= r_address_valid_buffer; - end - if (o_read_rq) begin - r_address_valid_buffer <= i_address_valid; - end - if (i_ack) begin - r_data_i_buffer <= i_data; - end - end - end - -endmodule diff --git a/fw/rtl/pc.v b/fw/rtl/pc.v deleted file mode 100644 index d3f8964..0000000 --- a/fw/rtl/pc.v +++ /dev/null @@ -1,289 +0,0 @@ -module pc ( - input i_clk, - input i_reset, - - input i_ftdi_clk, - input i_ftdi_cs, - input i_ftdi_do, - output reg o_ftdi_di, - - output reg o_read_rq, - output reg o_write_rq, - input i_ack, - output reg [31:0] o_address, - input [31:0] i_data, - output reg [31:0] o_data, - - input i_bus_active, - output reg o_n64_disable -); - - // Command ids - - localparam [7:0] CMD_STATUS = 8'h00; - localparam [7:0] CMD_CONFIG = 8'h10; - localparam [7:0] CMD_ADDR = 8'h20; - localparam [7:0] CMD_READ_LENGTH = 8'h30; - localparam [7:0] CMD_WRITE = 8'h40; - localparam [7:0] CMD_READ = 8'h50; - localparam [7:0] CMD_CART_RESET = 8'hFC; - localparam [7:0] CMD_FLUSH_WRITE = 8'hFD; - localparam [7:0] CMD_FLUSH_READ = 8'hFE; - localparam [7:0] CMD_SPI_RESET = 8'hFF; - - // SPI [de]serializer - - reg [4:0] r_spi_bit_counter; - reg [30:0] r_spi_i_shift; - reg [7:0] r_spi_cmd; - reg r_spi_cmd_valid; - - reg r_fifo_pc_to_bus_flush; - reg r_fifo_bus_to_pc_flush; - - reg r_fifo_pc_to_bus_rq; - reg r_fifo_bus_to_pc_rq; - - wire [31:0] w_fifo_bus_to_pc_data; - - wire [10:0] w_fifo_pc_to_bus_usedw; - wire [10:0] w_fifo_bus_to_pc_usedw; - - reg r_n64_disabled_ff1, r_n64_disabled_ff2; - reg r_address_inc_ff1, r_address_inc_ff2; - - wire [31:0] w_spi_status = { - 8'hAA, // Test control byte - r_address_inc_ff2, - r_n64_disabled_ff2, - w_fifo_pc_to_bus_usedw, - w_fifo_bus_to_pc_usedw, - }; - - // SPI bit control and command stage - - always @(posedge i_ftdi_clk or posedge i_ftdi_cs or posedge i_reset) begin - if (i_ftdi_cs || i_reset) begin - r_spi_bit_counter <= 5'd0; - r_spi_cmd <= 8'd0; - r_spi_cmd_valid <= 1'b0; - end else begin - r_spi_bit_counter <= r_spi_bit_counter + 5'd1; - if (&r_spi_bit_counter[2:0] && !r_spi_cmd_valid) begin - r_spi_bit_counter <= 5'd0; - r_spi_cmd <= {r_spi_i_shift[6:0], i_ftdi_do}; - r_spi_cmd_valid <= 1'b1; - end - end - end - - // SPI input shift register - - always @(posedge i_ftdi_clk) begin - r_spi_i_shift <= {r_spi_i_shift[29:0], i_ftdi_do}; - end - - // SPI command control signals - - always @(posedge i_ftdi_clk or posedge i_ftdi_cs or posedge i_reset) begin - if (i_ftdi_cs || i_reset) begin - r_fifo_pc_to_bus_flush <= 1'b0; - r_fifo_bus_to_pc_flush <= 1'b0; - r_fifo_pc_to_bus_rq <= 1'b0; - r_fifo_bus_to_pc_rq <= 1'b0; - end else begin - if (&r_spi_bit_counter[2:0] && !r_spi_cmd_valid) begin - case ({r_spi_i_shift[6:0], i_ftdi_do}) - CMD_FLUSH_WRITE: r_fifo_pc_to_bus_flush <= 1'b1; - CMD_FLUSH_READ: r_fifo_bus_to_pc_flush <= 1'b1; - CMD_SPI_RESET: begin - r_fifo_pc_to_bus_flush <= 1'b1; - r_fifo_bus_to_pc_flush <= 1'b1; - end - endcase - end - if (r_spi_bit_counter == 5'd30) begin - case (r_spi_cmd) - CMD_CONFIG, CMD_ADDR, CMD_READ_LENGTH, CMD_WRITE, CMD_CART_RESET: begin - r_fifo_pc_to_bus_rq <= 1'b1; - end - CMD_READ: r_fifo_bus_to_pc_rq <= 1'b1; - endcase - end else begin - r_fifo_pc_to_bus_rq <= 1'b0; - r_fifo_bus_to_pc_rq <= 1'b0; - end - end - end - - // SPI output data stage - - always @(negedge i_ftdi_clk or posedge i_ftdi_cs or posedge i_reset) begin - if (i_ftdi_cs || i_reset) begin - o_ftdi_di <= 1'b0; - end else begin - if (r_spi_cmd_valid) begin - case (r_spi_cmd) - CMD_STATUS: o_ftdi_di <= w_spi_status[5'd31 - r_spi_bit_counter]; - CMD_READ: o_ftdi_di <= w_fifo_bus_to_pc_data[5'd31 - r_spi_bit_counter]; - default: o_ftdi_di <= 1'b1; - endcase - end else begin - o_ftdi_di <= 1'b0; - end - end - end - - // sys_clk -> spi_clk signal synchronization - - reg r_address_inc; - - always @(posedge i_ftdi_clk) begin - {r_n64_disabled_ff2, r_n64_disabled_ff1} <= {r_n64_disabled_ff1, o_n64_disable}; - {r_address_inc_ff2, r_address_inc_ff1} <= {r_address_inc_ff1, r_address_inc}; - end - - // FIFOs - - reg r_fifo_pc_to_bus_rdreq; - wire [39:0] w_fifo_pc_to_bus_q; - wire w_fifo_pc_to_bus_rdempty; - - wire [7:0] w_fifo_pc_to_bus_cmd; - wire [31:0] w_fifo_pc_to_bus_data; - - assign {w_fifo_pc_to_bus_cmd, w_fifo_pc_to_bus_data} = w_fifo_pc_to_bus_q; - - wire w_fifo_bus_to_pc_wrfull; - - reg r_bus_read_in_progress; - - fifo_pc_to_bus fifo_pc_to_bus_inst ( - .aclr(r_fifo_pc_to_bus_flush), - - .wrclk(i_ftdi_clk || i_ftdi_cs), - .wrreq(r_fifo_pc_to_bus_rq), - .data({r_spi_cmd, r_spi_i_shift, i_ftdi_do}), - .wrusedw(w_fifo_pc_to_bus_usedw), - - .rdclk(i_clk), - .rdreq(r_fifo_pc_to_bus_rdreq), - .q(w_fifo_pc_to_bus_q), - .rdempty(w_fifo_pc_to_bus_rdempty) - ); - - fifo_bus_to_pc fifo_bus_to_pc_inst ( - .aclr(r_fifo_bus_to_pc_flush), - - .rdclk(i_ftdi_clk || i_ftdi_cs), - .rdreq(r_fifo_bus_to_pc_rq), - .q(w_fifo_bus_to_pc_data), - .rdusedw(w_fifo_bus_to_pc_usedw), - - .wrclk(i_clk), - .wrreq(i_ack && r_bus_read_in_progress), - .data(i_data), - .wrfull(w_fifo_bus_to_pc_wrfull), - ); - - // Bus controller - - reg r_cmd_config; - reg r_cmd_addr; - reg r_cmd_read_length; - reg r_cmd_write; - reg r_cmd_cart_reset; - - always @(posedge i_clk) begin - r_cmd_config <= w_fifo_pc_to_bus_cmd == CMD_CONFIG; - r_cmd_addr <= w_fifo_pc_to_bus_cmd == CMD_ADDR; - r_cmd_read_length <= w_fifo_pc_to_bus_cmd == CMD_READ_LENGTH; - r_cmd_write <= w_fifo_pc_to_bus_cmd == CMD_WRITE; - r_cmd_cart_reset <= w_fifo_pc_to_bus_cmd == CMD_CART_RESET; - end - - reg r_bus_read_pending_rq; - reg [23:0] r_bus_read_remaining_words; // Max 64 MB - - reg r_bus_write_in_progress; - - always @(posedge i_clk or posedge i_reset) begin - if (i_reset) begin - o_read_rq <= 1'b0; - o_write_rq <= 1'b0; - o_n64_disable <= 1'b0; - r_address_inc <= 1'b1; - r_fifo_pc_to_bus_rdreq <= 1'b0; - r_bus_read_pending_rq <= 1'b0; - r_bus_read_in_progress <= 1'b0; - r_bus_write_in_progress <= 1'b0; - end else begin - o_read_rq <= 1'b0; - o_write_rq <= 1'b0; - r_fifo_pc_to_bus_rdreq <= 1'b0; - - if (!w_fifo_pc_to_bus_rdempty && !r_fifo_pc_to_bus_rdreq && !r_bus_read_in_progress && !r_bus_write_in_progress) begin - if (r_cmd_config && !i_bus_active) begin - {r_address_inc, o_n64_disable} <= w_fifo_pc_to_bus_data[1:0]; - r_fifo_pc_to_bus_rdreq <= 1'b1; - end - if (r_cmd_addr) begin - o_address <= w_fifo_pc_to_bus_data; - r_fifo_pc_to_bus_rdreq <= 1'b1; - end - if (r_cmd_read_length) begin - if (o_n64_disable) begin - r_bus_read_pending_rq <= 1'b1; - r_bus_read_remaining_words <= w_fifo_pc_to_bus_data[23:0]; - r_bus_read_in_progress <= 1'b1; - end else begin - r_fifo_pc_to_bus_rdreq <= 1'b1; - end - end - if (r_cmd_write) begin - if (o_n64_disable) begin - o_write_rq <= 1'b1; - o_data <= w_fifo_pc_to_bus_data; - r_bus_write_in_progress <= 1'b1; - end else begin - r_fifo_pc_to_bus_rdreq <= 1'b1; - end - end - end - - if (i_ack) o_address[31:2] <= o_address[31:2] + r_address_inc; - - if (!w_fifo_bus_to_pc_wrfull && r_bus_read_pending_rq) begin - o_read_rq <= 1'b1; - r_bus_read_pending_rq <= 1'b0; - end - - if (i_ack && r_bus_read_in_progress) begin - if (r_bus_read_remaining_words > 24'd0) begin - r_bus_read_pending_rq <= 1'b1; - r_bus_read_remaining_words <= r_bus_read_remaining_words - 24'd1; - end else begin - r_fifo_pc_to_bus_rdreq <= 1'b1; - r_bus_read_in_progress <= 1'b0; - end - end - - if (i_ack && r_bus_write_in_progress) begin - r_fifo_pc_to_bus_rdreq <= 1'b1; - r_bus_write_in_progress <= 1'b0; - end - - if (!w_fifo_pc_to_bus_rdempty && !r_fifo_pc_to_bus_rdreq && r_cmd_cart_reset) begin - o_read_rq <= 1'b0; - o_write_rq <= 1'b0; - o_n64_disable <= 1'b0; - r_address_inc <= 1'b1; - r_fifo_pc_to_bus_rdreq <= 1'b1; - r_bus_read_pending_rq <= 1'b0; - r_bus_read_in_progress <= 1'b0; - r_bus_write_in_progress <= 1'b0; - end - end - end - -endmodule diff --git a/fw/rtl/sdram.v b/fw/rtl/sdram.v deleted file mode 100644 index e18b0e8..0000000 --- a/fw/rtl/sdram.v +++ /dev/null @@ -1,133 +0,0 @@ -module sdram ( - input i_clk, - input i_reset, - - output o_sdram_cs, - output o_sdram_ras, - output o_sdram_cas, - output o_sdram_we, - output [1:0] o_sdram_ba, - output [12:0] o_sdram_a, - input [15:0] i_sdram_dq, - output [15:0] o_sdram_dq, - output o_sdram_dq_mode, - - input i_select, - input i_read_rq, - input i_write_rq, - output reg o_ack, - input [31:0] i_address, - input [31:0] i_data, - output reg [31:0] o_data -); - - reg r_wb_cyc; - reg r_wb_stb; - - wire w_wb_cyc; - wire w_wb_stb; - wire [31:0] w_wb_addr; - wire w_wb_ack; - wire w_wb_stall; - wire [31:0] w_wb_o_data; - - assign w_wb_cyc = w_wb_stb || r_wb_cyc; - assign w_wb_stb = i_select && !w_wb_stall && (i_read_rq || i_write_rq || r_wb_stb); - - always @(posedge i_clk or posedge i_reset) begin - if (i_reset) begin - r_wb_cyc <= 1'b0; - end else begin - if (w_wb_stb) begin - r_wb_cyc <= 1'b1; - end else if (w_wb_ack) begin - r_wb_cyc <= 1'b0; - end - end - end - - // FIXME: Ugly solution for 16-bit address read align - - reg r_unaligned_access; - reg r_next_word; - - assign w_wb_addr = i_address + {r_next_word, 2'b00}; - - always @(posedge i_clk or posedge i_reset) begin - if (i_reset) begin - o_ack <= 1'b0; - r_wb_stb <= 1'b0; - r_unaligned_access <= 1'b0; - r_next_word <= 1'b0; - end else begin - o_ack <= 1'b0; - r_wb_stb <= 1'b0; - - if (i_read_rq) begin - if (!i_address[1]) begin - r_unaligned_access <= 1'b0; - r_next_word <= 1'b0; - end else begin - r_unaligned_access <= 1'b1; - r_next_word <= 1'b0; - end - end - if (w_wb_ack) begin - if (!r_unaligned_access) begin - o_data <= w_wb_o_data; - o_ack <= 1'b1; - end else begin - if (!r_next_word) begin - o_data[31:16] <= w_wb_o_data[15:0]; - r_wb_stb <= 1'b1; - r_next_word <= 1'b1; - end else begin - o_ack <= 1'b1; - o_data[15:0] <= w_wb_o_data[31:16]; - r_unaligned_access <= 1'b0; - r_next_word <= 1'b0; - end - end - end - end - end - - // Weird shift register required by this module - // https://github.com/ZipCPU/arrowzip/blob/master/rtl/arrowzip/toplevel.v#L176 - - reg [15:0] r_sdram_dq_ext_clk; - reg [15:0] r_sdram_dq; - - always @(posedge i_clk) begin - {r_sdram_dq, r_sdram_dq_ext_clk} <= {r_sdram_dq_ext_clk, i_sdram_dq}; - end - - wbsdram wbsdram_inst ( - .i_clk(i_clk), - - .i_wb_cyc(w_wb_cyc), - .i_wb_stb(w_wb_stb), // FIXME: Currently strobe can be missed when w_wb_stall is high - .i_wb_we(i_write_rq), - .i_wb_addr(w_wb_addr[31:2]), - .i_wb_data(i_data), - .i_wb_sel({4'b1111}), // No need to control this signal - .o_wb_ack(w_wb_ack), - .o_wb_stall(w_wb_stall), - .o_wb_data(w_wb_o_data), - - .o_ram_cs_n(o_sdram_cs), - // .o_ram_cke(), // No connection on PCB - .o_ram_ras_n(o_sdram_ras), - .o_ram_cas_n(o_sdram_cas), - .o_ram_we_n(o_sdram_we), - .o_ram_bs(o_sdram_ba), - .o_ram_addr(o_sdram_a), - .o_ram_dmod(o_sdram_dq_mode), - .i_ram_data(r_sdram_dq), - .o_ram_data(o_sdram_dq) - // .o_ram_dqm(), // No connection on PCB - - // .o_debug() // No need for this signal - ); - -endmodule diff --git a/fw/rtl/top.v b/fw/rtl/top.v index bbc33c2..001974e 100644 --- a/fw/rtl/top.v +++ b/fw/rtl/top.v @@ -1,10 +1,10 @@ module top ( input i_clk, - input i_ftdi_clk, - input i_ftdi_cs, - input i_ftdi_do, - output o_ftdi_di, + output o_ftdi_clk, + output o_ftdi_si, + input i_ftdi_so, + input i_ftdi_cts, input i_n64_nmi, input i_n64_reset, @@ -23,8 +23,8 @@ module top ( output o_sdram_clk, output o_sdram_cs, - output o_sdram_cas, output o_sdram_ras, + output o_sdram_cas, output o_sdram_we, output [1:0] o_sdram_ba, output [12:0] o_sdram_a, @@ -50,314 +50,277 @@ module top ( inout [7:0] io_pmod ); - // Clock and reset + // Clock and reset signals wire w_sys_clk; wire w_sdram_clk; wire w_pll_lock; wire w_sys_reset = ~w_pll_lock; - pll sys_pll( + + // PLL clock generator + + pll sys_pll ( .inclk0(i_clk), .c0(w_sys_clk), .c1(w_sdram_clk), .locked(w_pll_lock) ); - gpio_ddro sdram_clk_ddro( + + // SDRAM clock output + + gpio_ddro sdram_clk_ddro ( .outclock(w_sdram_clk), .outclocken(1'b1), .din({1'b0, 1'b1}), .pad_out(o_sdram_clk) ); - // Input synchronization - reg r_n64_nmi_ff1, r_n64_nmi_ff2; - reg r_n64_reset_ff1, r_n64_reset_ff2; + // Bank ids - reg r_n64_alel_ff1, r_n64_alel_ff2; - reg r_n64_aleh_ff1, r_n64_aleh_ff2; - reg r_n64_read_ff1, r_n64_read_ff2; - reg r_n64_write_ff1, r_n64_write_ff2; + localparam [3:0] BANK_INVALID = 4'd0; + localparam [3:0] BANK_ROM = 4'd1; + localparam [3:0] BANK_CART = 4'd2; + localparam [3:0] BANK_EEPROM = 4'd3; - reg r_n64_si_clk_ff1, r_n64_si_clk_ff2; - reg r_n64_si_dq_ff1, r_n64_si_dq_ff2; - always @(posedge w_sys_clk) begin - {r_n64_nmi_ff2, r_n64_nmi_ff1} <= {r_n64_nmi_ff1, i_n64_nmi}; - {r_n64_reset_ff2, r_n64_reset_ff1} <= {r_n64_reset_ff1, i_n64_reset}; + // N64 PI - {r_n64_alel_ff2, r_n64_alel_ff1} <= {r_n64_alel_ff1, i_n64_pi_alel}; - {r_n64_aleh_ff2, r_n64_aleh_ff1} <= {r_n64_aleh_ff1, i_n64_pi_aleh}; - {r_n64_read_ff2, r_n64_read_ff1} <= {r_n64_read_ff1, i_n64_pi_read}; - {r_n64_write_ff2, r_n64_write_ff1} <= {r_n64_write_ff1, i_n64_pi_write}; - - {r_n64_si_clk_ff2, r_n64_si_clk_ff1} <= {r_n64_si_clk_ff1, i_n64_si_clk}; - {r_n64_si_dq_ff2, r_n64_si_dq_ff1} <= {r_n64_si_dq_ff1, io_n64_si_dq}; - end - - // Tri-state connection management - - wire w_n64_pi_ad_mode; - wire [15:0] w_n64_pi_ad_o; - assign io_n64_pi_ad = w_n64_pi_ad_mode ? w_n64_pi_ad_o : 16'hZZZZ; - - wire w_n64_si_dq_o; - assign io_n64_si_dq = w_n64_si_dq_o ? 1'bZ : 1'b0; - - wire w_n64_cic_dq_o; - assign io_n64_cic_dq = w_n64_cic_dq_o ? 1'bZ : 1'b0; - - wire w_sdram_dq_mode; - wire [15:0] w_sdram_dq_o; - assign io_sdram_dq = w_sdram_dq_mode ? w_sdram_dq_o : 16'hZZZZ; - - wire w_sd_cmd_mode; - wire [1:0] w_sd_dat_mode; - wire w_sd_cmd_o; - wire [3:0] w_sd_dat_o; - assign io_sd_cmd = w_sd_cmd_mode ? w_sd_cmd_o : 1'bZ; - assign io_sd_dat = w_sd_dat_mode == 2'b00 ? {3'bZZZ, w_sd_dat_o[0]} : - w_sd_dat_mode == 2'b10 ? w_sd_dat_o : 4'bZZZZ; - - wire [1:0] w_flash_dq_mode; - wire [3:0] w_flash_dq_o; - assign io_flash_dq = w_flash_dq_mode == 2'b00 ? {3'bZZZ, w_flash_dq_o[0]} : - w_flash_dq_mode == 2'b10 ? w_flash_dq_o : 4'bZZZZ; - - wire [1:0] w_sram_dq_mode; - wire [3:0] w_sram_dq_o; - assign io_sram_dq = w_sram_dq_mode == 2'b00 ? {3'bZZZ, w_sram_dq_o[0]} : - w_sram_dq_mode == 2'b10 ? w_sram_dq_o : 4'bZZZZ; - - wire w_rtc_sda_o; - assign io_rtc_sda = w_rtc_sda_o ? 1'bZ : 1'b0; - - // Temporary assignments - - assign w_n64_cic_dq_o = 1'b1; - assign w_sd_cmd_mode = 1'b0; - assign w_sd_dat_mode = 2'b00; - assign w_sram_dq_mode = 2'b00; - assign w_rtc_sda_o = 1'b1; - assign io_pmod = 8'hZZ; - - // Modules connection - - wire w_n64_read_rq; - wire w_n64_write_rq; + wire w_n64_request; + wire w_n64_write; + wire w_n64_busy; wire w_n64_ack; - wire [31:0] w_n64_address; + wire [3:0] w_n64_bank; + wire [25:0] w_n64_address; wire [31:0] w_n64_i_data; wire [31:0] w_n64_o_data; - wire w_pc_read_rq; - wire w_pc_write_rq; - wire w_pc_ack; - wire [31:0] w_pc_address; - wire [31:0] w_pc_i_data; - wire [31:0] w_pc_o_data; + wire w_n64_busy_cart_control; + wire w_n64_ack_cart_control; + wire [31:0] w_n64_i_data_cart_control; - wire w_bus_read_rq; - wire w_bus_write_rq; - wire w_bus_ack; - wire [31:0] w_bus_address; - reg [31:0] r_bus_i_data; - wire [31:0] w_bus_o_data; + wire w_n64_busy_sdram; + wire w_n64_ack_sdram; + wire [31:0] w_n64_i_data_sdram; - wire w_n64_disable; + wire w_n64_busy_embedded_flash; + wire w_n64_ack_embedded_flash; + wire [31:0] w_n64_i_data_embedded_flash; - assign w_n64_ack = !w_n64_disable && w_bus_ack; - assign w_pc_ack = w_n64_disable && w_bus_ack; - - assign w_bus_read_rq = w_n64_disable ? w_pc_read_rq : w_n64_read_rq; - assign w_bus_write_rq = w_n64_disable ? w_pc_write_rq : w_n64_write_rq; - assign w_bus_address = w_n64_disable ? w_pc_address : w_n64_address; - assign w_bus_o_data = w_n64_disable ? w_pc_o_data : w_n64_o_data; - - wire w_cart_config_select; - wire w_flash_select; - wire w_flash_cfg_select; - wire w_sdram_select; - wire w_eeprom_select; - - wire w_flash_enable; - wire w_sdram_enable; - wire w_eeprom_pi_enable; - wire w_eeprom_enable; - wire w_eeprom_16k_enable; - - wire w_address_valid; - - wire w_cart_config_ack; - wire [31:0] w_cart_config_o_data; - - wire w_flash_ack; - wire [31:0] w_flash_o_data; - - wire w_sdram_ack; - wire [31:0] w_sdram_o_data; - - wire w_eeprom_ack; - wire [31:0] w_eeprom_o_data; - - reg r_empty_ack; - - assign w_bus_ack = w_cart_config_ack || w_flash_ack || w_sdram_ack || w_eeprom_ack || r_empty_ack; + wire w_n64_busy_eeprom; + wire w_n64_ack_eeprom; + wire [31:0] w_n64_i_data_eeprom; always @(*) begin - r_bus_i_data = 32'hFFFF_FFFF; - if (w_cart_config_select) r_bus_i_data = w_cart_config_o_data; - if (w_flash_select || w_flash_cfg_select) r_bus_i_data = w_flash_o_data; - if (w_sdram_select) r_bus_i_data = w_sdram_o_data; - if (w_eeprom_select) r_bus_i_data = w_eeprom_o_data; + w_n64_busy = w_n64_busy_cart_control || w_n64_busy_sdram || w_n64_busy_embedded_flash || w_n64_busy_eeprom; + w_n64_ack = w_n64_ack_cart_control || w_n64_ack_sdram || w_n64_ack_embedded_flash || w_n64_ack_eeprom; + w_n64_i_data = 32'h0000_0000; + if (w_n64_ack_cart_control) w_n64_i_data = w_n64_i_data_cart_control; + if (w_n64_ack_sdram) w_n64_i_data = w_n64_i_data_sdram; + if (w_n64_ack_embedded_flash) w_n64_i_data = w_n64_i_data_embedded_flash; + if (w_n64_ack_eeprom) w_n64_i_data = w_n64_i_data_eeprom; end - always @(posedge w_sys_clk) begin - r_empty_ack <= !w_address_valid && (w_bus_read_rq || w_bus_write_rq); - end - - // Bus activity signal - - reg r_bus_active; - wire w_bus_active = r_bus_active && !w_bus_ack; - - always @(posedge w_sys_clk or posedge w_sys_reset) begin - if (w_sys_reset) begin - r_bus_active <= 1'b0; - end else begin - if (w_bus_read_rq || w_bus_write_rq) r_bus_active <= 1'b1; - if (w_bus_ack) r_bus_active <= 1'b0; - end - end - - // Modules - - pc pc_inst ( - .i_clk(w_sys_clk), - .i_reset(w_sys_reset), - - .i_ftdi_clk(i_ftdi_clk), - .i_ftdi_cs(i_ftdi_cs), - .i_ftdi_do(i_ftdi_do), - .o_ftdi_di(o_ftdi_di), - - .o_read_rq(w_pc_read_rq), - .o_write_rq(w_pc_write_rq), - .i_ack(w_pc_ack), - .o_address(w_pc_address), - .i_data(r_bus_i_data), - .o_data(w_pc_o_data), - - .i_bus_active(w_bus_active), - .o_n64_disable(w_n64_disable) - ); - n64_pi n64_pi_inst ( .i_clk(w_sys_clk), - .i_reset(~r_n64_reset_ff2), + .i_reset(w_sys_reset), - .i_n64_pi_alel({i_n64_pi_alel, r_n64_alel_ff2}), - .i_n64_pi_aleh({i_n64_pi_aleh, r_n64_aleh_ff2}), - .i_n64_pi_read(r_n64_read_ff2), - .i_n64_pi_write(r_n64_write_ff2), - .i_n64_pi_ad(io_n64_pi_ad), - .o_n64_pi_ad(w_n64_pi_ad_o), - .o_n64_pi_ad_mode(w_n64_pi_ad_mode), + .i_n64_reset(i_n64_reset), + .i_n64_pi_alel(i_n64_pi_alel), + .i_n64_pi_aleh(i_n64_pi_aleh), + .i_n64_pi_read(i_n64_pi_read), + .i_n64_pi_write(i_n64_pi_write), + .io_n64_pi_ad(io_n64_pi_ad), - .o_read_rq(w_n64_read_rq), - .o_write_rq(w_n64_write_rq), + .o_request(w_n64_request), + .o_write(w_n64_write), + .i_busy(w_n64_busy), .i_ack(w_n64_ack), + .o_bank(w_n64_bank), .o_address(w_n64_address), - .i_data(r_bus_i_data), - .o_data(w_n64_o_data), - - .i_address_valid(w_address_valid) + .i_data(w_n64_i_data), + .o_data(w_n64_o_data) ); - n64_si n64_si_inst ( + + // PC USB + + wire w_pc_request; + wire w_pc_write; + wire w_pc_busy; + wire w_pc_ack; + wire [3:0] w_pc_bank; + wire [25:0] w_pc_address; + wire [31:0] w_pc_i_data; + wire [31:0] w_pc_o_data; + + wire w_pc_busy_cart_control; + wire w_pc_ack_cart_control; + wire [31:0] w_pc_i_data_cart_control; + + wire w_pc_busy_sdram; + wire w_pc_ack_sdram; + wire [31:0] w_pc_i_data_sdram; + + wire w_pc_busy_eeprom; + wire w_pc_ack_eeprom; + wire [31:0] w_pc_i_data_eeprom; + + always @(*) begin + w_pc_busy = w_pc_busy_cart_control || w_pc_busy_sdram || w_pc_busy_eeprom; + w_pc_ack = w_pc_ack_cart_control || w_pc_ack_sdram || w_pc_ack_eeprom; + w_pc_i_data = 32'h0000_0000; + if (w_pc_ack_cart_control) w_pc_i_data = w_pc_i_data_cart_control; + if (w_pc_ack_sdram) w_pc_i_data = w_pc_i_data_sdram; + if (w_pc_ack_eeprom) w_pc_i_data = w_pc_i_data_eeprom; + end + + usb_pc usb_pc_inst ( + .i_clk(w_sys_clk), + .i_reset(w_sys_reset), + + .o_ftdi_clk(o_ftdi_clk), + .o_ftdi_si(o_ftdi_si), + .i_ftdi_so(i_ftdi_so), + .i_ftdi_cts(i_ftdi_cts), + + .o_request(w_pc_request), + .o_write(w_pc_write), + .i_busy(w_pc_busy), + .i_ack(w_pc_ack), + .o_bank(w_pc_bank), + .o_address(w_pc_address), + .i_data(w_pc_i_data), + .o_data(w_pc_o_data) + ); + + + // Cart interface + + wire w_cart_control_request; + wire w_cart_control_write; + wire w_cart_control_busy; + wire w_cart_control_ack; + wire [25:0] w_cart_control_address; + wire [31:0] w_cart_control_o_data; + wire [31:0] w_cart_control_i_data; + + wire w_rom_switch; + wire w_eeprom_enable; + wire w_eeprom_16k_mode; + + device_arbiter device_arbiter_cart_control_inst ( .i_clk(w_sys_clk), .i_reset(w_sys_reset), - .i_si_clk(r_n64_si_clk_ff2), - .i_si_reset(~r_n64_reset_ff2), - .i_si_dq(r_n64_si_dq_ff2), - .o_si_dq(w_n64_si_dq_o), - - .i_eeprom_select(w_eeprom_select), - .i_read_rq(w_bus_read_rq), - .i_write_rq(w_bus_write_rq), - .o_ack(w_eeprom_ack), - .i_address(w_bus_address), - .i_data(w_bus_o_data), - .o_data(w_eeprom_o_data), - - .i_eeprom_enable(w_eeprom_enable), - .i_eeprom_16k_enable(w_eeprom_16k_enable) + .i_request_pri(w_n64_request), + .i_write_pri(w_n64_write), + .o_busy_pri(w_n64_busy_cart_control), + .o_ack_pri(w_n64_ack_cart_control), + .i_bank_pri(w_n64_bank), + .i_address_pri(w_n64_address[25:2]), + .o_data_pri(w_n64_i_data_cart_control), + .i_data_pri(w_n64_o_data), + + .i_request_sec(w_pc_request), + .i_write_sec(w_pc_write), + .o_busy_sec(w_pc_busy_cart_control), + .o_ack_sec(w_pc_ack_cart_control), + .i_bank_sec(w_pc_bank), + .i_address_sec(w_pc_address[25:2]), + .o_data_sec(w_pc_i_data_cart_control), + .i_data_sec(w_pc_o_data), + + .o_request(w_cart_control_request), + .o_write(w_cart_control_write), + .i_busy(w_cart_control_busy), + .i_ack(w_cart_control_ack), + .o_address(w_cart_control_address), + .i_data(w_cart_control_o_data), + .o_data(w_cart_control_i_data) ); + defparam device_arbiter_cart_control_inst.DEVICE_BANK = BANK_CART; - address_decoder address_decoder_inst ( - .i_address(w_bus_address), - - .o_cart_config(w_cart_config_select), - .o_flash(w_flash_select), - .o_flash_cfg(w_flash_cfg_select), - .o_sdram(w_sdram_select), - .o_eeprom(w_eeprom_select), - - .i_flash_enable(w_flash_enable), - .i_sdram_enable(w_sdram_enable), - .i_eeprom_pi_enable(w_eeprom_pi_enable), - - .o_address_valid(w_address_valid) - ); - - cart_config cart_config_inst ( + cart_control cart_control_inst ( .i_clk(w_sys_clk), .i_reset(w_sys_reset), - .i_n64_reset(~r_n64_reset_ff2), - .i_n64_nmi(~r_n64_nmi_ff2), + .i_n64_reset(i_n64_reset), + .i_n64_nmi(i_n64_nmi), - .i_select(w_cart_config_select), - .i_read_rq(w_bus_read_rq), - .i_write_rq(w_bus_write_rq), - .o_ack(w_cart_config_ack), - .i_address(w_bus_address), - .i_data(w_bus_o_data), - .o_data(w_cart_config_o_data), - - .i_n64_disabled(w_n64_disable), - - .o_flash_enable(w_flash_enable), - .o_sdram_enable(w_sdram_enable), - .o_eeprom_pi_enable(w_eeprom_pi_enable), + .i_request(w_cart_control_request), + .i_write(w_cart_control_write), + .o_busy(w_cart_control_busy), + .o_ack(w_cart_control_ack), + .i_address(w_cart_control_address), + .o_data(w_cart_control_o_data), + .i_data(w_cart_control_i_data), + + .o_rom_switch(w_rom_switch), .o_eeprom_enable(w_eeprom_enable), - .o_eeprom_16k_enable(w_eeprom_16k_enable) + .o_eeprom_16k_mode(w_eeprom_16k_mode) ); - flash flash_inst ( + + // Embedded flash + + memory_embedded_flash memory_embedded_flash_inst ( .i_clk(w_sys_clk), .i_reset(w_sys_reset), - .o_flash_clk(o_flash_clk), - .o_flash_cs(o_flash_cs), - .i_flash_dq(io_flash_dq), - .o_flash_dq(w_flash_dq_o), - .o_flash_dq_mode(w_flash_dq_mode), - - .i_select(w_flash_select), - .i_cfg_select(w_flash_cfg_select), - .i_read_rq(w_bus_read_rq), - .i_write_rq(w_bus_write_rq), - .o_ack(w_flash_ack), - .i_address(w_bus_address), - .i_data(w_bus_o_data), - .o_data(w_flash_o_data) + .i_request(!w_rom_switch && w_n64_request && !w_n64_write && w_n64_bank == BANK_ROM), + .o_busy(w_n64_busy_embedded_flash), + .o_ack(w_n64_ack_embedded_flash), + .i_address(w_n64_address[25:2]), + .o_data(w_n64_i_data_embedded_flash) ); - sdram sdram_inst ( + + // SDRAM + + wire w_sdram_request; + wire w_sdram_write; + wire w_sdram_busy; + wire w_sdram_ack; + wire [25:0] w_sdram_address; + wire [31:0] w_sdram_o_data; + wire [31:0] w_sdram_i_data; + + device_arbiter device_arbiter_sdram_inst ( + .i_clk(w_sys_clk), + .i_reset(w_sys_reset), + + .i_request_pri(w_rom_switch && w_n64_request), + .i_write_pri(w_n64_write), + .o_busy_pri(w_n64_busy_sdram), + .o_ack_pri(w_n64_ack_sdram), + .i_bank_pri(w_n64_bank), + .i_address_pri(w_n64_address[25:1]), + .o_data_pri(w_n64_i_data_sdram), + .i_data_pri(w_n64_o_data), + + .i_request_sec(w_pc_request), + .i_write_sec(w_pc_write), + .o_busy_sec(w_pc_busy_sdram), + .o_ack_sec(w_pc_ack_sdram), + .i_bank_sec(w_pc_bank), + .i_address_sec(w_pc_address[25:1]), + .o_data_sec(w_pc_i_data_sdram), + .i_data_sec(w_pc_o_data), + + .o_request(w_sdram_request), + .o_write(w_sdram_write), + .i_busy(w_sdram_busy), + .i_ack(w_sdram_ack), + .o_address(w_sdram_address), + .i_data(w_sdram_o_data), + .o_data(w_sdram_i_data) + ); + defparam device_arbiter_sdram_inst.DEVICE_BANK = BANK_ROM; + + memory_sdram memory_sdram_inst ( .i_clk(w_sys_clk), .i_reset(w_sys_reset), @@ -367,30 +330,78 @@ module top ( .o_sdram_we(o_sdram_we), .o_sdram_ba(o_sdram_ba), .o_sdram_a(o_sdram_a), - .i_sdram_dq(io_sdram_dq), - .o_sdram_dq(w_sdram_dq_o), - .o_sdram_dq_mode(w_sdram_dq_mode), + .io_sdram_dq(io_sdram_dq), - .i_select(w_sdram_select), - .i_read_rq(w_bus_read_rq), - .i_write_rq(w_bus_write_rq), + .i_request(w_sdram_request), + .i_write(w_sdram_write), + .o_busy(w_sdram_busy), .o_ack(w_sdram_ack), - .i_address(w_bus_address), - .i_data(w_bus_o_data), - .o_data(w_sdram_o_data) + .i_address(w_sdram_address), + .o_data(w_sdram_o_data), + .i_data(w_sdram_i_data) ); - // LED - localparam ROLLING_LED_WIDTH = 8; + // EEPROM 4/16k - reg [(ROLLING_LED_WIDTH-1):0] r_rolling_led; + wire w_eeprom_request; + wire w_eeprom_write; + wire w_eeprom_busy; + wire w_eeprom_ack; + wire [25:0] w_eeprom_address; + wire [31:0] w_eeprom_o_data; + wire [31:0] w_eeprom_i_data; - assign o_led = |r_rolling_led; + device_arbiter device_arbiter_eeprom_inst ( + .i_clk(w_sys_clk), + .i_reset(w_sys_reset), + + .i_request_pri(w_n64_request), + .i_write_pri(w_n64_write), + .o_busy_pri(w_n64_busy_eeprom), + .o_ack_pri(w_n64_ack_eeprom), + .i_bank_pri(w_n64_bank), + .i_address_pri(w_n64_address[25:2]), + .o_data_pri(w_n64_i_data_eeprom), + .i_data_pri(w_n64_o_data), + + .i_request_sec(w_pc_request), + .i_write_sec(w_pc_write), + .o_busy_sec(w_pc_busy_eeprom), + .o_ack_sec(w_pc_ack_eeprom), + .i_bank_sec(w_pc_bank), + .i_address_sec(w_pc_address[25:2]), + .o_data_sec(w_pc_i_data_eeprom), + .i_data_sec(w_pc_o_data), + + .o_request(w_eeprom_request), + .o_write(w_eeprom_write), + .i_busy(w_eeprom_busy), + .i_ack(w_eeprom_ack), + .o_address(w_eeprom_address), + .i_data(w_eeprom_o_data), + .o_data(w_eeprom_i_data) + ); + defparam device_arbiter_eeprom_inst.DEVICE_BANK = BANK_EEPROM; - always @(posedge w_sys_clk or posedge w_sys_reset) begin - if (w_sys_reset) r_rolling_led <= {(ROLLING_LED_WIDTH){1'b0}}; - else r_rolling_led <= {r_rolling_led[(ROLLING_LED_WIDTH-2):0], w_bus_active}; - end + n64_si n64_si_inst ( + .i_clk(w_sys_clk), + .i_reset(w_sys_reset), + + .i_n64_reset(i_n64_reset), + .i_n64_si_clk(i_n64_si_clk), + .io_n64_si_dq(io_n64_si_dq), + + .i_request(w_eeprom_request), + .i_write(w_eeprom_write), + .o_busy(w_eeprom_busy), + .o_ack(w_eeprom_ack), + .i_address(w_eeprom_address), + .i_data(w_eeprom_i_data), + .o_data(w_eeprom_o_data), + + .i_eeprom_enable(w_eeprom_enable), + .i_eeprom_16k_mode(w_eeprom_16k_mode) + ); endmodule diff --git a/fw/rtl/usb/ftdi_fsi.v b/fw/rtl/usb/ftdi_fsi.v new file mode 100644 index 0000000..c2f1f82 --- /dev/null +++ b/fw/rtl/usb/ftdi_fsi.v @@ -0,0 +1,116 @@ +module usb_ftdi_fsi ( + input i_clk, + input i_reset, + + output reg o_ftdi_clk, + output reg o_ftdi_si, + input i_ftdi_so, + input i_ftdi_cts, + + input i_rx_ready, + output reg o_rx_valid, + output reg o_rx_channel, + output reg [7:0] o_rx_data, + + output reg o_tx_busy, + input i_tx_valid, + input i_tx_channel, + input [7:0] i_tx_data +); + + // Output clock generation and control + + always @(posedge i_clk) begin + if (i_reset || !i_rx_ready) begin + o_ftdi_clk <= 1'b1; + end else begin + o_ftdi_clk <= ~o_ftdi_clk; + end + end + + + // RX module + + reg r_rx_in_progress; + reg [3:0] r_rx_bit_counter; + reg r_tx_start_bit; + reg r_rx_tx_contention; + + always @(posedge i_clk) begin + o_rx_valid <= 1'b0; + + if (i_reset) begin + r_rx_in_progress <= 1'b0; + end else begin + if (!o_ftdi_clk) begin + if (!r_rx_in_progress) begin + r_rx_in_progress <= !i_ftdi_so; + r_rx_bit_counter <= 4'd0; + r_rx_tx_contention <= r_tx_start_bit; + end else begin + r_rx_bit_counter <= r_rx_bit_counter + 4'd1; + + if (!r_rx_bit_counter[3]) begin + o_rx_data <= {i_ftdi_so, o_rx_data[7:1]}; + end else begin + r_rx_in_progress <= 1'b0; + o_rx_valid <= !r_rx_tx_contention; + o_rx_channel <= i_ftdi_so; + end + end + end + end + end + + + // TX module + + reg r_tx_pending; + reg [3:0] r_tx_bit_counter; + reg [7:0] r_tx_data; + reg r_tx_channel; + + wire w_tx_request_op = i_tx_valid && !o_tx_busy; + wire w_tx_pending_op = !o_ftdi_clk || !i_ftdi_cts || !i_rx_ready || r_rx_in_progress; + wire w_tx_reset_output_op = o_ftdi_clk && !o_tx_busy; + wire w_tx_start_op = (w_tx_request_op || r_tx_pending) && !w_tx_pending_op; + wire w_tx_shift_op = o_ftdi_clk && o_tx_busy && !r_tx_pending; + + always @(posedge i_clk) begin + r_tx_start_bit <= 1'b0; + + if (i_reset) begin + o_ftdi_si <= 1'b1; + o_tx_busy <= 1'b0; + r_tx_pending <= 1'b0; + end else begin + if (w_tx_request_op) begin + o_tx_busy <= 1'b1; + r_tx_data <= i_tx_data; + r_tx_channel <= i_tx_channel; + r_tx_pending <= w_tx_pending_op; + end + + if (w_tx_reset_output_op) begin + o_ftdi_si <= 1'b1; + end + + if (w_tx_start_op) begin + o_ftdi_si <= 1'b0; + r_tx_start_bit <= 1'b1; + r_tx_pending <= 1'b0; + r_tx_bit_counter <= 4'd0; + end + + if (w_tx_shift_op) begin + r_tx_bit_counter <= r_tx_bit_counter + 4'd1; + {r_tx_data[6:0], o_ftdi_si} <= r_tx_data; + if (r_tx_bit_counter[3]) begin + o_ftdi_si <= r_tx_channel; + o_tx_busy <= 1'b0; + end + end + end + end + +endmodule diff --git a/fw/rtl/usb/pc.v b/fw/rtl/usb/pc.v new file mode 100644 index 0000000..0f2147e --- /dev/null +++ b/fw/rtl/usb/pc.v @@ -0,0 +1,379 @@ +module usb_pc ( + input i_clk, + input i_reset, + + output o_ftdi_clk, + output o_ftdi_si, + input i_ftdi_so, + input i_ftdi_cts, + + output reg o_request, + output reg o_write, + input i_busy, + input i_ack, + output reg [3:0] o_bank, + output reg [25:0] o_address, + input [31:0] i_data, + output reg [31:0] o_data +); + + // FTDI transport + + reg r_ftdi_rx_ready; + wire w_ftdi_rx_valid; + wire [7:0] w_ftdi_rx_data; + + wire w_ftdi_tx_busy; + reg r_ftdi_tx_valid; + reg [7:0] r_ftdi_tx_data; + + usb_ftdi_fsi usb_ftdi_fsi_inst ( + .i_clk(i_clk), + .i_reset(i_reset), + + .o_ftdi_clk(o_ftdi_clk), + .o_ftdi_si(o_ftdi_si), + .i_ftdi_so(i_ftdi_so), + .i_ftdi_cts(i_ftdi_cts), + + .i_rx_ready(r_ftdi_rx_ready), + .o_rx_valid(w_ftdi_rx_valid), + .o_rx_data(w_ftdi_rx_data), + + .o_tx_busy(w_ftdi_tx_busy), + .i_tx_valid(r_ftdi_tx_valid), + .i_tx_channel(1'b1), // Channel B + .i_tx_data(r_ftdi_tx_data) + ); + + + // Command ids + + localparam byte CMD_TRIGGER [0:2] = '{"C", "M", "D"}; + + localparam byte CMD_IDENTIFY = "I"; + localparam byte CMD_READ = "R"; + localparam byte CMD_WRITE = "W"; + localparam byte CMD_DEBUG_MODE = "D"; + localparam byte CMD_DEBUG_WRITE = "F"; + + localparam [1:0] RX_STAGE_CMD = 2'd0; + localparam [1:0] RX_STAGE_PARAM = 2'd1; + localparam [1:0] RX_STAGE_DATA = 2'd2; + localparam [1:0] RX_STAGE_IGNORE = 2'd3; + + + // RX module + + reg [1:0] r_rx_stage; + reg [2:0] r_rx_byte_counter; + reg [7:0] r_rx_cmd; + reg r_rx_buffer_valid; + reg r_rx_param_valid; + reg [63:0] r_rx_buffer; + + reg [19:0] r_data_items_remaining; + + reg r_tx_cmd_valid; + reg [7:0] r_tx_cmd; + reg r_tx_done; + + always @(posedge i_clk) begin + r_rx_buffer_valid <= 1'b0; + r_rx_param_valid <= 1'b0; + r_tx_cmd_valid <= 1'b0; + + if (i_reset) begin + r_rx_stage <= RX_STAGE_CMD; + r_rx_byte_counter <= 3'd0; + end else begin + if (w_ftdi_rx_valid) begin + r_rx_byte_counter <= r_rx_byte_counter + 3'd1; + + case (r_rx_stage) + RX_STAGE_CMD: begin + if (w_ftdi_rx_data != CMD_TRIGGER[r_rx_byte_counter]) begin + r_rx_byte_counter <= 3'd0; + end + + if (r_rx_byte_counter == 3'd3) begin + r_rx_byte_counter <= 3'd0; + r_rx_cmd <= w_ftdi_rx_data; + + case (w_ftdi_rx_data) + CMD_IDENTIFY: begin + r_rx_stage <= RX_STAGE_IGNORE; + r_tx_cmd_valid <= 1'b1; + r_tx_cmd <= w_ftdi_rx_data; + end + + CMD_READ: r_rx_stage <= RX_STAGE_PARAM; + + CMD_WRITE: r_rx_stage <= RX_STAGE_PARAM; + + CMD_DEBUG_MODE: r_rx_stage <= RX_STAGE_PARAM; + + CMD_DEBUG_WRITE: r_rx_stage <= RX_STAGE_DATA; + + default: r_rx_stage <= RX_STAGE_CMD; + endcase + end + end + + RX_STAGE_PARAM: begin + r_rx_buffer <= {r_rx_buffer[55:0], w_ftdi_rx_data}; + + case (r_rx_cmd) + CMD_READ: begin + if (r_rx_byte_counter == 3'd7) begin + r_rx_stage <= RX_STAGE_IGNORE; + r_rx_byte_counter <= 3'd0; + r_rx_param_valid <= 1'b1; + r_tx_cmd_valid <= 1'b1; + r_tx_cmd <= r_rx_cmd; + end + end + + CMD_WRITE: begin + if (r_rx_byte_counter == 3'd7) begin + r_rx_stage <= RX_STAGE_DATA; + r_rx_byte_counter <= 3'd0; + r_rx_param_valid <= 1'b1; + end + end + + CMD_DEBUG_MODE: begin + if (r_rx_byte_counter == 3'd3) begin + r_rx_stage <= RX_STAGE_CMD; + r_rx_byte_counter <= 3'd0; + r_rx_param_valid <= 1'b1; + end + end + + CMD_DEBUG_WRITE: begin + if (r_rx_byte_counter == 3'd3) begin + r_rx_stage <= RX_STAGE_DATA; + r_rx_byte_counter <= 3'd0; + r_rx_param_valid <= 1'b1; + end + end + + default: begin + r_rx_stage <= RX_STAGE_CMD; + r_rx_byte_counter <= 3'd0; + end + endcase + end + + RX_STAGE_DATA: begin + r_rx_buffer <= {r_rx_buffer[55:0], w_ftdi_rx_data}; + + case (r_rx_cmd) + CMD_WRITE: begin + if (r_rx_byte_counter == 3'd3) begin + if (r_data_items_remaining == 20'd0) begin + r_rx_stage <= RX_STAGE_IGNORE; + r_tx_cmd_valid <= 1'b1; + r_tx_cmd <= r_rx_cmd; + end + r_rx_byte_counter <= 3'd0; + r_rx_buffer_valid <= 1'b1; + end + end + + CMD_DEBUG_WRITE: begin + if (r_data_items_remaining == 20'd0) begin + r_rx_stage <= RX_STAGE_CMD; + end + r_rx_buffer_valid <= 1'b1; + end + + default: begin + r_rx_stage <= RX_STAGE_CMD; + r_rx_byte_counter <= 3'd0; + end + endcase + end + + RX_STAGE_IGNORE: begin + end + + default: begin + r_rx_stage <= RX_STAGE_CMD; + r_rx_byte_counter <= 3'd0; + end + endcase + end + + if (r_tx_done) begin + r_rx_stage <= RX_STAGE_CMD; + r_rx_byte_counter <= 3'd0; + end + end + end + + + // Command parameter decoder + + reg r_tx_debug_enabled; + + always @(posedge i_clk) begin + if (i_reset) begin + r_tx_debug_enabled <= 1'b0; + end else begin + if (r_rx_param_valid) begin + case (r_rx_cmd) + CMD_READ, CMD_WRITE: begin + o_address <= {r_rx_buffer[63:34], 2'b00}; + o_bank <= r_rx_buffer[27:24]; + r_data_items_remaining <= r_rx_buffer[19:0]; + end + + CMD_DEBUG_MODE: begin + r_tx_debug_enabled <= r_rx_buffer[0]; + end + + CMD_DEBUG_WRITE: begin + r_data_items_remaining <= r_rx_buffer[19:0]; + end + endcase + end + + if (o_request && !i_busy && r_data_items_remaining > 20'd0) begin + o_address[25:2] <= o_address[25:2] + 1'd1; + r_data_items_remaining <= r_data_items_remaining - 1'd1; + end + end + end + + + // TX module + + localparam byte IDENTIFY_STRING [0:3] = '{"S", "6", "4", "a"}; + localparam byte RSP_COMPLETE [0:2] = '{"C", "M", "P"}; + + localparam [1:0] TX_STAGE_IDLE = 2'd0; + localparam [1:0] TX_STAGE_DATA = 2'd1; + localparam [1:0] TX_STAGE_RESPONSE = 2'd2; + + reg [1:0] r_tx_stage; + reg [1:0] r_tx_byte_counter; + + reg [31:0] r_i_data_buffer; + + always @(*) begin + r_ftdi_tx_data = 8'h00; + + case (r_tx_stage) + TX_STAGE_DATA: begin + case (r_tx_cmd) + CMD_IDENTIFY: r_ftdi_tx_data = IDENTIFY_STRING[r_tx_byte_counter]; + CMD_READ: r_ftdi_tx_data = r_i_data_buffer[(r_tx_byte_counter * 8) -: 8]; + endcase + end + + TX_STAGE_RESPONSE: begin + if (r_tx_byte_counter != 2'd3) r_ftdi_tx_data = RSP_COMPLETE[r_tx_byte_counter]; + else r_ftdi_tx_data = r_tx_cmd; + end + endcase + end + + wire w_tx_successful = r_ftdi_tx_valid && !w_ftdi_tx_busy; + + always @(posedge i_clk) begin + r_ftdi_tx_valid <= 1'b0; + r_tx_done <= 1'b0; + + if (i_reset) begin + r_tx_stage <= TX_STAGE_IDLE; + end else begin + if (w_tx_successful) r_tx_byte_counter <= r_tx_byte_counter + 2'd1; + + case (r_tx_stage) + TX_STAGE_IDLE: begin + r_tx_byte_counter <= 2'd0; + + if (r_tx_cmd_valid) begin + r_ftdi_tx_valid <= 1'b1; + + case (r_tx_cmd) + CMD_IDENTIFY: r_tx_stage <= TX_STAGE_DATA; + default: r_tx_stage <= TX_STAGE_RESPONSE; + endcase + end + end + + TX_STAGE_DATA: begin + r_ftdi_tx_valid <= 1'b1; + + if (r_tx_byte_counter == 2'd3 && w_tx_successful) begin + case (r_tx_cmd) + CMD_IDENTIFY: begin + r_tx_stage <= TX_STAGE_RESPONSE; + end + + CMD_READ: begin + if (r_data_items_remaining == 20'd0) begin + r_tx_stage <= TX_STAGE_RESPONSE; + end + end + + default: r_tx_stage <= TX_STAGE_RESPONSE; + endcase + end + end + + TX_STAGE_RESPONSE: begin + r_ftdi_tx_valid <= 1'b1; + + if (r_tx_byte_counter == 2'd3 && w_tx_successful) begin + r_tx_stage <= TX_STAGE_IDLE; + r_tx_done <= 1'b1; + end + end + + default: r_tx_stage <= TX_STAGE_IDLE; + endcase + end + end + + + // Bus controller + + always @(posedge i_clk) begin + o_request <= 1'b0; + o_write <= 1'b0; + + if (i_reset) begin + r_ftdi_rx_ready <= 1'b1; + end else begin + case (r_rx_cmd) + CMD_WRITE: begin + if ((r_rx_buffer_valid || !r_ftdi_rx_ready) && !i_busy) begin + o_request <= 1'b1; + o_write <= 1'b1; + o_data <= r_rx_buffer[31:0]; + r_ftdi_rx_ready <= 1'b1; + end + + if (o_request && i_busy) begin + o_request <= 1'b1; + o_write <= 1'b1; + end + + if (r_rx_buffer_valid && i_busy) begin + r_ftdi_rx_ready <= 1'b0; + end + end + endcase + + // TODO: Read from bus + // case (r_tx_cmd) + // CMD_READ: begin + // end + // endcase + end + end + +endmodule diff --git a/hw/.gitignore b/hw/.gitignore index 0e05548..110ec6d 100644 --- a/hw/.gitignore +++ b/hw/.gitignore @@ -1,3 +1,4 @@ +/CAMOutputs *.b#* *.l#* *.s#* diff --git a/hw/SummerCart64.brd b/hw/SummerCart64.brd index 6a1940b..6bc246f 100644 --- a/hw/SummerCart64.brd +++ b/hw/SummerCart64.brd @@ -333,7 +333,7 @@ Designed by Polprzewodnikowy >VALUE - + @@ -2529,7 +2529,7 @@ design rules under a new name. - + @@ -2537,7 +2537,7 @@ design rules under a new name. - + @@ -2546,7 +2546,7 @@ design rules under a new name. - + @@ -2555,7 +2555,7 @@ design rules under a new name. - + @@ -2564,7 +2564,7 @@ design rules under a new name. - + @@ -2573,7 +2573,7 @@ design rules under a new name. - + @@ -2582,7 +2582,7 @@ design rules under a new name. - + @@ -2591,15 +2591,15 @@ design rules under a new name. - + - + - + @@ -2609,11 +2609,11 @@ design rules under a new name. - + - + @@ -2622,7 +2622,7 @@ design rules under a new name. - + @@ -2631,7 +2631,7 @@ design rules under a new name. - + @@ -2648,107 +2648,107 @@ design rules under a new name. - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2766,7 +2766,7 @@ design rules under a new name. - + @@ -2777,7 +2777,7 @@ design rules under a new name. - + @@ -2789,47 +2789,47 @@ design rules under a new name. - + - + - + - + - + - + - + - + @@ -2839,27 +2839,27 @@ design rules under a new name. - + - + - + - + - + @@ -2873,13 +2873,13 @@ design rules under a new name. - + - + @@ -2888,126 +2888,126 @@ design rules under a new name. - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -3016,7 +3016,7 @@ design rules under a new name. - + diff --git a/hw/SummerCart64.cam b/hw/SummerCart64.cam new file mode 100644 index 0000000..3c5f3aa --- /dev/null +++ b/hw/SummerCart64.cam @@ -0,0 +1,276 @@ +{ + "author": { + "email": "sc@mateuszfaderewski.pl", + "name": "Polprzewodnikowy" + }, + "description": { + "EN": "SummerCart64 CAM job." + }, + "output_type": "zip", + "outputs": [ + { + "filename_prefix": "CAMOutputs/GerberFiles", + "format_specifier": { + "decimal": 4, + "integer": 3 + }, + "generate_job_file": true, + "output_type": "gerber", + "outputs": [ + { + "advanced_options": { + "mirror": false, + "offset_x": 0, + "offset_y": 0, + "rotate": false, + "upside_down": false + }, + "board_outline": false, + "config": { + "file_function": "Copper", + "layer": 1, + "layer_details": "mixed", + "layer_type": "top" + }, + "filename_format": "%PREFIX/copper_top.gbr", + "layers": [ + 1, + 17, + 18 + ], + "name": "Top Copper", + "polarity": "positive", + "type": "gerber_layer" + }, + { + "advanced_options": { + "mirror": false, + "offset_x": 0, + "offset_y": 0, + "rotate": false, + "upside_down": false + }, + "board_outline": false, + "config": { + "file_function": "Copper", + "layer": 2, + "layer_details": "mixed", + "layer_type": "bottom" + }, + "filename_format": "%PREFIX/copper_bottom.gbr", + "layers": [ + 16, + 17, + 18 + ], + "name": "Bottom Copper", + "polarity": "positive", + "type": "gerber_layer" + }, + { + "advanced_options": { + "mirror": false, + "offset_x": 0, + "offset_y": 0, + "rotate": false, + "upside_down": false + }, + "board_outline": true, + "config": { + "file_function": "Profile", + "plating": "non-plated" + }, + "filename_format": "%PREFIX/profile.gbr", + "layers": [ + ], + "milling": true, + "polarity": "positive", + "type": "gerber_layer" + }, + { + "advanced_options": { + "mirror": false, + "offset_x": 0, + "offset_y": 0, + "rotate": false, + "upside_down": false + }, + "board_outline": false, + "config": { + "file_function": "Soldermask", + "index": 1, + "layer_type": "top" + }, + "filename_format": "%PREFIX/soldermask_top.gbr", + "layers": [ + 29 + ], + "name": "Soldermask Top", + "polarity": "positive", + "type": "gerber_layer" + }, + { + "advanced_options": { + "mirror": false, + "offset_x": 0, + "offset_y": 0, + "rotate": false, + "upside_down": false + }, + "board_outline": false, + "config": { + "file_function": "Soldermask", + "index": 1, + "layer_type": "bottom" + }, + "filename_format": "%PREFIX/soldermask_bottom.gbr", + "layers": [ + 30 + ], + "name": "Soldermask Bottom", + "polarity": "positive", + "type": "gerber_layer" + }, + { + "advanced_options": { + "mirror": false, + "offset_x": 0, + "offset_y": 0, + "rotate": false, + "upside_down": false + }, + "board_outline": false, + "config": { + "file_function": "Paste", + "layer_type": "top" + }, + "filename_format": "%PREFIX/solderpaste_top.gbr", + "layers": [ + 31 + ], + "milling": false, + "name": "Solderpaste Top", + "polarity": "positive", + "type": "gerber_layer" + }, + { + "advanced_options": { + "mirror": false, + "offset_x": 0, + "offset_y": 0, + "rotate": false, + "upside_down": false + }, + "board_outline": false, + "config": { + "file_function": "Paste", + "layer_type": "bottom" + }, + "filename_format": "%PREFIX/solderpaste_bottom.gbr", + "layers": [ + 32 + ], + "milling": false, + "name": "Solderpaste Bottom", + "polarity": "positive", + "type": "gerber_layer" + }, + { + "advanced_options": { + "mirror": false, + "offset_x": 0, + "offset_y": 0, + "rotate": false, + "upside_down": false + }, + "board_outline": false, + "config": { + "file_function": "Legend", + "index": 1, + "layer_type": "top" + }, + "filename_format": "%PREFIX/silkscreen_top.gbr", + "layers": [ + 21, + 25 + ], + "milling": false, + "name": "Silkscreen Top", + "polarity": "positive", + "type": "gerber_layer" + }, + { + "advanced_options": { + "mirror": false, + "offset_x": 0, + "offset_y": 0, + "rotate": false, + "upside_down": false + }, + "board_outline": false, + "config": { + "file_function": "Legend", + "index": 1, + "layer_type": "bottom" + }, + "filename_format": "%PREFIX/silkscreen_bottom.gbr", + "layers": [ + 22, + 26 + ], + "milling": false, + "name": "Silkscreen Bottom", + "polarity": "positive", + "type": "gerber_layer" + } + ], + "version": "RS274X" + }, + { + "filename_prefix": "CAMOutputs/DrillFiles", + "format_specifier": { + "decimal": 3, + "integer": 3 + }, + "output_type": "drill", + "outputs": [ + { + "advanced_options": { + "mirror": false, + "offset_x": 0, + "offset_y": 0, + "rotate": false, + "upside_down": false + }, + "filename_format": "%DRILLPREFIX/drill_%FROM_%TO.xln", + "name": "Auto Drill", + "type": "autodrills" + } + ] + }, + { + "filename_prefix": "CAMOutputs/Assembly", + "output_type": "assembly", + "outputs": [ + { + "filename_format": "%ASSEMBLYPREFIX/%N", + "list_attribute": true, + "list_type": "values", + "name": "Bill of Material", + "output_format": "txt", + "type": "bom" + } + ] + }, + { + "filename_prefix": "CAMOutputs/DrawingFiles", + "output_type": "drawing", + "outputs": [ + ] + } + ], + "timestamp": "2020-10-25T01:41:35", + "type": "EAGLE CAM job", + "units": "metric", + "version": "9.2.0" +} diff --git a/hw/SummerCart64.lbr b/hw/SummerCart64.lbr index 9a741c0..50e4a74 100644 --- a/hw/SummerCart64.lbr +++ b/hw/SummerCart64.lbr @@ -229,7 +229,7 @@ >VALUE - + diff --git a/hw/SummerCart64.sch b/hw/SummerCart64.sch index 84a0279..daba0c9 100644 --- a/hw/SummerCart64.sch +++ b/hw/SummerCart64.sch @@ -235,7 +235,7 @@ >VALUE - + @@ -13937,13 +13937,13 @@ Source: http://www.st.com/stonline/products/literature/ds/7194/ld1117axx.pdf - - - - - - - + + + + + + + @@ -13958,20 +13958,19 @@ Source: http://www.st.com/stonline/products/literature/ds/7194/ld1117axx.pdf - - - - + + + - + - + - - + + @@ -13979,802 +13978,861 @@ Source: http://www.st.com/stonline/products/literature/ds/7194/ld1117axx.pdf - + - - - - - - - - - - - + + + + + + + + + + + - - - - + + + + - - + + - - - + - + - - + + - + - - - - - - + + + + + - - + - - - + + + - + - - + + - - - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + +RTC +LED +CIC + + + + +PMOD + + + + + + + + +FLASH +SRAM + + + + +FTDI EEPROM + + + + +N64 <-> FPGA Resistors + + + + +N64 Edge Connector + + + + +Crystal Oscillators + + + + +SDRAM + + + + +FTDI <-> CIC Buffer + + + + +FPGA + + + + +Pull-up Resistors + + + + +SD Card + + + + +FTDI - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + - - - + + + - - - + + + - - - + + - - + + + - - - + + + - - - + + - - + + - - + + + - - - + + + - - - + + + - - - + + + - - - + + - - + + - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + - - + + + - - - + + + - - - + + - - + + - - + + - - + + - - + + + - - - + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - + + + - - - + + + - - + + + - - - + + + - - - + + + - - - + + + - - - + + - - - + + - - + + - - + + - - + + + - - + + + - - - + + - - - + + + - - + + + - - - + + - - - + + + - - + + - - - + + + - - + + + - - - + + + - - - + + + - - + + + - - - + + + - - - + + + - - - + + - - - + + + - - + + + - - - + + + - - + + - - - + + + - - - + + + - - - + + - - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + - - + + + - - + + - - - + + + - - - + + + - - + + + - - - + + + - - - + + + - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + - - - + + + - - - + + + - - - + + + - - + + + - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -14783,178 +14841,178 @@ Source: http://www.st.com/stonline/products/literature/ds/7194/ld1117axx.pdf - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - - + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -14962,2923 +15020,2842 @@ Source: http://www.st.com/stonline/products/literature/ds/7194/ld1117axx.pdf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - + + + + + + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - + + + + + + + - - + + - - - - + + + + - - + + - - - - + + + + - - + + - - - - + + + - - + - - + + - - + + - - + + - - - - + + + + - + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - + - - - - + + + + - - - - - - - - - - + + + - - - + + + - - - - + + + + + + + + - - - + + + - - + + - - - - + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - + + - - - - - + - - - + + - - - + + - - - + + + - + - - - - + + + + - - + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - - + + + - - + - - - - + + + + - - - + + + - - + - - - - + + + + + + + + + + + + + - + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + + + - - + - - - - + + + + - - + - - - - + + + - - + - - + - - - - + + + + - + - - - - + + + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - + + + + + + + + - - - - - + + + + + + + - - + + - - + + - - - + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + - - - - - - - - - - - - - - - - - - + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - + + + - - - + + + - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - + + + - - - + + + - - - - - - - - - - - + - - - + - + - - + + - + - + - - - - + + + - - - - - - - - + + + - - - - + + + - - - - - - - + - - - - + + + + - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - + + + - + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - + + - + - + - - - - - - + + + + - + - - - + + + - - + + - - - + + + - - - + + + - - - - + - diff --git a/hw/ftdi-template.xml b/hw/ftdi-template.xml new file mode 100644 index 0000000..50922cc Binary files /dev/null and b/hw/ftdi-template.xml differ diff --git a/sw/SummerBanger64/.gitignore b/sw/SummerBanger64/.gitignore deleted file mode 100644 index 3af5c0b..0000000 --- a/sw/SummerBanger64/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/__pycache__ -/roms -*.z64 diff --git a/sw/SummerBanger64/README.md b/sw/SummerBanger64/README.md deleted file mode 100644 index 4c20443..0000000 --- a/sw/SummerBanger64/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# SummerBanger64 - -A PC communication program for SummerCart64. - -## TODO - -- Expand documentation diff --git a/sw/SummerBanger64/SummerBanger64.py b/sw/SummerBanger64/SummerBanger64.py deleted file mode 100644 index d3f62a3..0000000 --- a/sw/SummerBanger64/SummerBanger64.py +++ /dev/null @@ -1,393 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import progressbar -import struct -import sys -import time -from pyftdi.spi import SpiController - - - -class SummerBanger64: - - # Command ids - - __CMD_STATUS = 0x00 - __CMD_CONFIG = 0x10 - __CMD_ADDR = 0x20 - __CMD_READ_LENGTH = 0x30 - __CMD_WRITE = 0x40 - __CMD_READ = 0x50 - __CMD_CART_RESET = 0xFC - __CMD_FLUSH_WRITE = 0xFD - __CMD_FLUSH_READ = 0xFE - __CMD_SPI_RESET = 0xFF - - # Size declarations - - __FIFO_SIZE = 1024 - __FIFO_SIZE_BYTES = __FIFO_SIZE * 4 - __FLASH_PAGE_SIZE = 256 - - # Cart addresses - - __SDRAM_ADDRESS = 0x10000000 - __FLASH_ADDRESS = 0x18000000 - __FLASH_CFG_ADDRESS = 0x1C000000 - __CART_CONFIG_ADDRESS = 0x1E000000 - __CIC_TYPE_ADDRESS = 0x1E000004 - - # Cart config register bits - - __FLASH_ENABLE = (1 << 0) - __SDRAM_ENABLE = (1 << 1) - - - def __init__(self, config = {}): - self.__spi_controller = SpiController(cs_count=1) - self.__spi_controller.configure('ftdi://ftdi:2232h/1') - self.__spi = self.__spi_controller.get_port(0, float(config['spi_speed'])) - - self.__update_progress = config['update_progress'] - - if (not config['no_init']): - self.__reset() - self.__set_config(n64_disable=True, address_increment=True) - self.__write_word(self.__CART_CONFIG_ADDRESS, (self.__SDRAM_ENABLE | self.__FLASH_ENABLE)) - - self.__config = config - - - def __del__(self): - if (not self.__config['no_init']): - self.__reset() - self.__spi_controller.terminate() - - - def __get_status(self, field=None): - status_word = struct.unpack('>I', self.__spi.exchange([self.__CMD_STATUS], 4))[0] - status = { - 'status': hex(status_word), - 'control_byte': (status_word & (0xFF << 24)) >> 24, - 'address_increment': (status_word & (0x1 << 23)) >> 23, - 'n64_disabled': (status_word & (0x1 << 22)) >> 22, - 'write_used': (status_word & (0x7FF << 11)) >> 11, - 'read_used': (status_word & (0x7FF << 0)) >> 0, - } - return status.get(field) if field else status - - - def __set_config(self, n64_disable=False, address_increment=True): - config = ( - ((1 if address_increment else 0) << 1) | - ((1 if n64_disable else 0) << 0) - ) - self.__spi.write(bytearray([self.__CMD_CONFIG]) + struct.pack('>I', int(config))) - - - def __set_address(self, address): - self.__spi.write(bytearray([self.__CMD_ADDR]) + struct.pack('>I', int(address))) - - - def __set_read_length(self, length): - self.__spi.write(bytearray([self.__CMD_READ_LENGTH]) + struct.pack('>I', int(length - 1))) - - - def __read_data(self, length): - return self.__spi.exchange([self.__CMD_READ], int(length * 4)) - - - def __write_data(self, data): - self.__spi.write(bytearray([self.__CMD_WRITE]) + data) - - - def __cart_reset(self): - self.__spi.write([self.__CMD_CART_RESET, 0x00, 0x00, 0x00, 0x00]) - - - def __spi_reset(self): - self.__spi.write([self.__CMD_SPI_RESET]) - - - def __reset(self): - self.__spi_reset() - self.__cart_reset() - - - def __wait_for_read_fill(self, length): - while (self.__get_status('read_used') < int(length)): - pass - - - def __wait_for_write_space(self, length): - while ((self.__FIFO_SIZE - self.__get_status('write_used')) < int(length)): - pass - - - def __wait_for_write_completion(self): - while (self.__get_status('write_used') > 0): - pass - - - def __read_word(self, address): - self.__set_address(address) - self.__set_read_length(1) - self.__wait_for_read_fill(1) - return struct.unpack('>I', self.__read_data(1))[0] - - - def __write_word(self, address, data): - self.__set_address(address) - self.__wait_for_write_space(1) - self.__write_data(struct.pack('>I', data)) - - - def __flash_operation(self, byte, mode='s', direction='w', select=False, cfg_mode=True): - return int( - ((1 if cfg_mode else 0) << 12) | - ((1 if mode == 'q' else 0) << 11) | - ((1 if direction == 'w' else 0) << 9) | - ((1 if select else 0) << 8) | - (byte & 0xFF) - ) - - - def __flash_create_operation(self, byte, mode='s', direction='w', select=False, cfg_mode=True): - return bytearray(struct.pack('>I', self.__flash_operation(byte, mode, direction, select, cfg_mode))) - - - def __flash_issue_operation(self, byte, mode='s', direction='w', select=False, cfg_mode=True): - self.__write_word(self.__FLASH_CFG_ADDRESS, self.__flash_operation(byte, mode, direction, select, cfg_mode)) - return ((self.__read_word(self.__FLASH_CFG_ADDRESS) & 0xFF) if direction == 'r' else 0x00).to_bytes(1, 'big') - - - def __flash_exchange(self, command, address=None, data=[], length=0, address_mode='s', write_mode='s', read_mode='s', cfg_mode=True): - operations = bytearray([]) - - operations += self.__flash_create_operation(command) - - if (address != None): - for byte in bytearray(struct.pack('>I', address))[1:4]: - operations += self.__flash_create_operation(byte, mode=address_mode) - - for byte in data: - operations += self.__flash_create_operation(byte, mode=write_mode) - - if (not length): - operations += self.__flash_create_operation(0x00, select=True, cfg_mode=cfg_mode) - - self.__set_address(self.__FLASH_CFG_ADDRESS) - self.__write_data(operations) - self.__wait_for_write_completion() - - data_read = bytearray([]) - - for _ in range(length): - data_read += self.__flash_issue_operation(0x00, mode=read_mode, direction='r') - - if (length > 0): - self.__flash_issue_operation(0x00, select=True, cfg_mode=cfg_mode) - - return data_read - - - def __flash_exit_xip(self): - self.__flash_exchange(0xFF, data=[0xFF, 0xFF]) - - - def __flash_enter_xip(self): - self.__flash_exchange( - 0xEB, - address=0, - data=[0xA0, 0x00, 0x00], - length=1, - address_mode='q', - write_mode='q', - read_mode='q', - cfg_mode=False - ) - - - def __flash_read_status(self): - return self.__flash_exchange(0x05, length=1)[0] - - - def __flash_write_enable(self): - self.__flash_exchange(0x06) - - - def __flash_write_disable(self): - self.__flash_exchange(0x04) - - - def __flash_sector_erase(self, address): - self.__flash_exchange(0x20, address=address) - - - def __flash_chip_erase(self): - self.__flash_exchange(0xC7) - self.__flash_exchange(0x60) - - - def __flash_program_page(self, address, data): - self.__flash_exchange(0x32, address=address, data=data, address_mode='s', write_mode='q') - - - def __flash_wait_for_not_busy(self): - while (self.__flash_read_status() & 0x01): - pass - - - def __flash_wait_for_write_enable_latch(self): - while (not (self.__flash_read_status() & 0x02)): - pass - - - def __calculate_length_in_words(self, length): - return int((length + 3) / 4) - - - def __get_chunk_iterator(self, data, chunk_size): - for i in range(0, len(data), chunk_size): - yield (data[i:i + chunk_size], i) - - - def print_status(self): - print(self.__get_status()) - - - def read_rom(self, length, from_flash=False): - length_in_words = self.__calculate_length_in_words(length) - chunk_size = int((self.__FIFO_SIZE_BYTES * 3) / 4) - data = bytearray([]) - - self.__set_address(self.__FLASH_ADDRESS if from_flash else self.__SDRAM_ADDRESS) - self.__set_read_length(length_in_words) - - while (length > 0): - current_chunk_size = min(length, chunk_size) - read_length_in_words = self.__calculate_length_in_words(current_chunk_size) - self.__wait_for_read_fill(read_length_in_words) - data += self.__read_data(read_length_in_words) - length -= current_chunk_size - if (self.__update_progress): self.__update_progress(len(data)) - - return data - - - def write_sdram(self, data): - length = len(data) - chunk_size = int((self.__FIFO_SIZE_BYTES * 3) / 4) - - self.__set_address(self.__SDRAM_ADDRESS) - - for (chunk, offset) in self.__get_chunk_iterator(data, chunk_size): - current_chunk_size = min(chunk_size, length - offset) - self.__wait_for_write_space(self.__calculate_length_in_words(current_chunk_size)) - self.__write_data(chunk) - if (self.__update_progress): self.__update_progress(offset) - - if (self.__update_progress): self.__update_progress(length) - - self.__wait_for_write_completion() - - - def write_flash(self, data): - self.__set_config(n64_disable=True, address_increment=False) - - self.__flash_exit_xip() - - print('\rErasing Flash, this may take a while...', end=' ') - - self.__flash_write_enable() - self.__flash_wait_for_write_enable_latch() - self.__flash_chip_erase() - self.__flash_wait_for_not_busy() - - print('Done') - - for (page, offset) in self.__get_chunk_iterator(data, self.__FLASH_PAGE_SIZE): - self.__flash_write_enable() - self.__flash_wait_for_write_enable_latch() - self.__flash_program_page(offset, page) - self.__flash_wait_for_not_busy() - if (self.__update_progress): self.__update_progress(offset) - - self.__flash_write_disable() - - self.__flash_enter_xip() - - self.__set_config(n64_disable=True, address_increment=True) - - - def set_cic_type(self, cic_type=0): - cic_lut = { - 5101: 0x11, - 6101: 0x12, - 6102: 0x13, - 6103: 0x14, - 6105: 0x15, - 6106: 0x16, - 7101: 0x03, - 7102: 0x02, - 7103: 0x04, - 7105: 0x05, - 7106: 0x06, - 8303: 0x07, - } - self.__write_word(self.__CIC_TYPE_ADDRESS, int(cic_lut.get(cic_type) or 0)) - - - -if __name__ == "__main__": - formatter = lambda prog: argparse.HelpFormatter(prog, max_help_position=30) - parser = argparse.ArgumentParser(description='Write/Read N64 ROM to SummerCart', formatter_class=formatter) - parser.add_argument('-s', '--speed', default=30E6, required=False, help='set SPI communication speed (in Hz, 30MHz max)') - parser.add_argument('-r', '--read', action='store_true', required=False, help='read ROM instead of writing') - parser.add_argument('-l', '--length', required=False, help='specify ROM length to read (in bytes)', default=2**26) - parser.add_argument('-f', '--flash', action='store_true', required=False, help='use Flash instead of SDRAM') - parser.add_argument('-c', '--cic', type=int, required=False, help='set CIC type to use by bootloader') - parser.add_argument('-q', '--status', action='store_true', required=False, help='just query and print the status word') - parser.add_argument('rom', help='path to ROM file (only .z64 files are supported)', nargs='?') - args = parser.parse_args() - - if ((len(sys.argv) == 1) or (not args.rom and (args.read or args.flash))): - parser.print_help() - parser.exit() - - bar = progressbar.DataTransferBar() - - def update_progress(length): - bar.update(length) - - config = { - 'no_init': args.status, - 'spi_speed': args.speed, - 'update_progress': update_progress, - } - - banger = SummerBanger64(config) - - if (args.cic != None): - banger.set_cic_type(args.cic) - elif (args.rom and not args.flash): - banger.set_cic_type() - - if (args.status): - banger.print_status() - elif (args.rom): - if (args.read): - with open(args.rom, 'wb') as f: - length = int(args.length) - bar.max_value = length - data = banger.read_rom(length, from_flash=args.flash) - f.write(data) - else: - with open(args.rom, 'rb') as f: - data = f.read() - bar.max_value = len(data) - if (args.flash): - banger.write_flash(data) - else: - banger.write_sdram(data) diff --git a/sw/SummerBanger64/bang_flash.bat b/sw/SummerBanger64/bang_flash.bat deleted file mode 100644 index 7d7b7e9..0000000 --- a/sw/SummerBanger64/bang_flash.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -cd /d "%~dp0" -python SummerBanger64.py -f "%~1" diff --git a/sw/SummerBanger64/bang_sdram.bat b/sw/SummerBanger64/bang_sdram.bat deleted file mode 100644 index 384f8de..0000000 --- a/sw/SummerBanger64/bang_sdram.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -cd /d "%~dp0" -python SummerBanger64.py "%~1" diff --git a/sw/SummerBanger64/requirements.txt b/sw/SummerBanger64/requirements.txt deleted file mode 100644 index e2f93e0..0000000 --- a/sw/SummerBanger64/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pyftdi>=0.51.2 -progressbar2>=3.53.1 diff --git a/sw/SummerLoader64/.gitignore b/sw/bootloader/.gitignore similarity index 100% rename from sw/SummerLoader64/.gitignore rename to sw/bootloader/.gitignore diff --git a/sw/SummerLoader64/Makefile b/sw/bootloader/Makefile similarity index 91% rename from sw/SummerLoader64/Makefile rename to sw/bootloader/Makefile index 6f4715d..927c6db 100644 --- a/sw/SummerLoader64/Makefile +++ b/sw/bootloader/Makefile @@ -19,7 +19,7 @@ SRC_FILES = $(wildcard $(patsubst %, %/*.c, . $(SRC_DIRS))) OBJ_FILES = $(addprefix build/, $(notdir $(SRC_FILES:.c=.o))) VPATH = $(SRC_DIRS) -ROM_SIZE = 1028k +ROM_SIZE = 90k ifeq ($(N64_BYTE_SWAP),true) ROM_EXTENSION = .v64 @@ -41,6 +41,7 @@ build/$(PROG_NAME)$(ROM_EXTENSION): build/$(PROG_NAME).elf rm -f build/$(PROG_NAME)$(ROM_EXTENSION) $(N64TOOL) $(N64_FLAGS) -t $(PROG_NAME) build/$(PROG_NAME).bin $(CHKSUM64PATH) build/$(PROG_NAME)$(ROM_EXTENSION) + $(OBJCOPY) build/$(PROG_NAME)$(ROM_EXTENSION) build/$(PROG_NAME).hex -I binary -O ihex build/$(PROG_NAME).elf: $(OBJ_FILES) $(LD) -o build/$(PROG_NAME).elf $(OBJ_FILES) $(LINK_FLAGS) diff --git a/sw/SummerLoader64/README.md b/sw/bootloader/README.md similarity index 100% rename from sw/SummerLoader64/README.md rename to sw/bootloader/README.md diff --git a/sw/SummerLoader64/src/boot.c b/sw/bootloader/src/boot.c similarity index 80% rename from sw/SummerLoader64/src/boot.c rename to sw/bootloader/src/boot.c index a134e4f..f07c68e 100644 --- a/sw/SummerLoader64/src/boot.c +++ b/sw/bootloader/src/boot.c @@ -66,7 +66,7 @@ tv_type_t boot_get_tv_type(cart_header_t *cart_header) { } } -void boot(cic_type_t cic_type, tv_type_t tv_type) { +void boot(cart_header_t *cart_header, cic_type_t cic_type, tv_type_t tv_type) { tv_type_t os_tv_type = tv_type == E_TV_TYPE_UNKNOWN ? OS_BOOT_CONFIG->tv_type : tv_type; volatile uint64_t gpr_regs[32]; @@ -95,36 +95,29 @@ void boot(cic_type_t cic_type, tv_type_t tv_type) { AI->dram_addr = 0; AI->len = 0; - while (SP->status & SP_STATUS_DMA_BUSY); + PI->dom1_lat = cart_header->pi_conf & 0xFF; + PI->dom1_pwd = cart_header->pi_conf >> 8; + PI->dom1_pgs = cart_header->pi_conf >> 16; + PI->dom1_rls = cart_header->pi_conf >> 20; if (DP_CMD->status & DP_CMD_STATUS_XBUS_DMEM_DMA) { while (DP_CMD->status & DP_CMD_STATUS_PIPE_BUSY); } - DP_CMD->status = DP_CMD_STATUS_CLEAR_FLUSH | DP_CMD_STATUS_CLEAR_FREEZE | DP_CMD_STATUS_CLEAR_XBUS_DMEM_DMA; - - for (size_t i = 0; i < ARRAY_ITEMS(SP_MEM->dmem); i++) { - SP_MEM->dmem[i] = CART[i]; + for (size_t i = 0; i < ARRAY_ITEMS(cart_header->boot_code); i++) { + SP_MEM->dmem[16 + i] = cart_header->boot_code[i]; } - while (PI->status & (PI_STATUS_IO_BUSY | PI_STATUS_DMA_BUSY)); + // CIC X105 based games checks if start of IPL2 code exists in SP IMEM - PI->status = PI_STATUS_CLEAR_INTERRUPT | PI_STATUS_RESET_CONTROLLER; - - for (size_t i = 0; i < ARRAY_ITEMS(SP_MEM->imem); i++) { - SP_MEM->imem[i] = 0; - } - - if (cic_type == E_CIC_TYPE_X105) { - SP_MEM->imem[0] = 0x3C0DBFC0; - SP_MEM->imem[1] = os_tv_type == E_TV_TYPE_PAL ? 0xBDA807FC : 0x8DA807FC; - SP_MEM->imem[2] = 0x25AD07C0; - SP_MEM->imem[3] = 0x31080080; - SP_MEM->imem[4] = 0x5500FFFC; - SP_MEM->imem[5] = 0x3C0DBFC0; - SP_MEM->imem[6] = 0x8DA80024; - SP_MEM->imem[7] = 0x3C0BB000; - } + SP_MEM->imem[0] = 0x3C0DBFC0; // lui t5, 0xBFC0 + SP_MEM->imem[1] = 0x8DA807FC; // lw t0, 0x07FC(t5) + SP_MEM->imem[2] = 0x25AD07C0; // addiu t5, t5, 0x07C0 + SP_MEM->imem[3] = 0x31080080; // andi t0, t0, 0x0080 + SP_MEM->imem[4] = 0x5500FFFC; // bnel t0, zero, &SP_MEM->imem[0] + SP_MEM->imem[5] = 0x3C0DBFC0; // lui t5, 0xBFC0 + SP_MEM->imem[6] = 0x8DA80024; // lw t0, 0x0024(t5) + SP_MEM->imem[7] = 0x3C0BB000; // lui t3, 0xB000 if (cic_type == E_CIC_TYPE_X105) { OS_BOOT_CONFIG->mem_size_6105 = OS_BOOT_CONFIG->mem_size; @@ -139,7 +132,7 @@ void boot(cic_type_t cic_type, tv_type_t tv_type) { gpr_regs[CPU_REG_S4] = os_tv_type; gpr_regs[CPU_REG_S5] = OS_BOOT_CONFIG->reset_type; gpr_regs[CPU_REG_S6] = BOOT_SEED_IPL3(cic_seeds[cic_type]); - gpr_regs[CPU_REG_S7] = (os_tv_type == E_TV_TYPE_PAL) ? OS_BOOT_VERSION_PAL : OS_BOOT_VERSION_NTSC; + gpr_regs[CPU_REG_S7] = BOOT_SEED_OS_VERSION(cic_seeds[cic_type]); gpr_regs[CPU_REG_SP] = CPU_ADDRESS_IN_REG(SP_MEM->imem[ARRAY_ITEMS(SP_MEM->imem) - 4]); gpr_regs[CPU_REG_RA] = CPU_ADDRESS_IN_REG(SP_MEM->imem[(os_tv_type == E_TV_TYPE_PAL) ? 341 : 340]); diff --git a/sw/SummerLoader64/src/boot.h b/sw/bootloader/src/boot.h similarity index 59% rename from sw/SummerLoader64/src/boot.h rename to sw/bootloader/src/boot.h index 249ac6f..3e87968 100644 --- a/sw/SummerLoader64/src/boot.h +++ b/sw/bootloader/src/boot.h @@ -1,24 +1,27 @@ #ifndef BOOT_H__ #define BOOT_H__ -#define BOOT_CRC32_5101 (0x587BD543) -#define BOOT_CRC32_6101 (0x6170A4A1) -#define BOOT_CRC32_7102 (0x009E9EA3) -#define BOOT_CRC32_X102 (0x90BB6CB5) -#define BOOT_CRC32_X103 (0x0B050EE0) -#define BOOT_CRC32_X105 (0x98BC2C86) -#define BOOT_CRC32_X106 (0xACC8580A) -#define BOOT_CRC32_8303 (0x0E018159) +#include -#define BOOT_SEED_5101 (0x0000AC00) -#define BOOT_SEED_X101 (0x00043F3F) -#define BOOT_SEED_X102 (0x00003F3F) -#define BOOT_SEED_X103 (0x0000783F) -#define BOOT_SEED_X105 (0x0000913F) -#define BOOT_SEED_X106 (0x0000853F) -#define BOOT_SEED_8303 (0x0000DD00) +#define BOOT_CRC32_5101 (0x587BD543) +#define BOOT_CRC32_6101 (0x6170A4A1) +#define BOOT_CRC32_7102 (0x009E9EA3) +#define BOOT_CRC32_X102 (0x90BB6CB5) +#define BOOT_CRC32_X103 (0x0B050EE0) +#define BOOT_CRC32_X105 (0x98BC2C86) +#define BOOT_CRC32_X106 (0xACC8580A) +#define BOOT_CRC32_8303 (0x0E018159) -#define BOOT_SEED_IPL3(x) (((x) & 0x0000FF00) >> 8) +#define BOOT_SEED_5101 (0x0000AC00) +#define BOOT_SEED_X101 (0x00043F3F) +#define BOOT_SEED_X102 (0x00003F3F) +#define BOOT_SEED_X103 (0x0000783F) +#define BOOT_SEED_X105 (0x0000913F) +#define BOOT_SEED_X106 (0x0000853F) +#define BOOT_SEED_8303 (0x0000DD00) + +#define BOOT_SEED_IPL3(x) (((x) & 0x0000FF00) >> 8) +#define BOOT_SEED_OS_VERSION(x) (((x) & 0x00040000) >> 18) typedef enum cic_type_e { E_CIC_TYPE_UNKNOWN, @@ -79,12 +82,9 @@ typedef struct os_boot_config_s os_boot_config_t; #define OS_BOOT_ROM_TYPE_GAME_PAK (0) #define OS_BOOT_ROM_TYPE_DD (1) -#define OS_BOOT_VERSION_NTSC (0) -#define OS_BOOT_VERSION_PAL (6) - cart_header_t *boot_load_cart_header(void); cic_type_t boot_get_cic_type(cart_header_t *cart_header); tv_type_t boot_get_tv_type(cart_header_t *cart_header); -void boot(cic_type_t cic_type, tv_type_t tv_type); +void boot(cart_header_t *cart_header, cic_type_t cic_type, tv_type_t tv_type); #endif diff --git a/sw/SummerLoader64/src/crc32.c b/sw/bootloader/src/crc32.c similarity index 97% rename from sw/SummerLoader64/src/crc32.c rename to sw/bootloader/src/crc32.c index 5f99f8b..fb2a42b 100644 --- a/sw/SummerLoader64/src/crc32.c +++ b/sw/bootloader/src/crc32.c @@ -1,5 +1,3 @@ -#include - #include "crc32.h" const uint32_t crc_table[256] = { diff --git a/sw/SummerLoader64/src/crc32.h b/sw/bootloader/src/crc32.h similarity index 79% rename from sw/SummerLoader64/src/crc32.h rename to sw/bootloader/src/crc32.h index 07c625c..6974c2b 100644 --- a/sw/SummerLoader64/src/crc32.h +++ b/sw/bootloader/src/crc32.h @@ -1,6 +1,7 @@ #ifndef CRC32_H__ #define CRC32_H__ +#include #include uint32_t crc32_calculate(void *buffer, size_t length); diff --git a/sw/SummerLoader64/src/main.c b/sw/bootloader/src/main.c similarity index 70% rename from sw/SummerLoader64/src/main.c rename to sw/bootloader/src/main.c index 5b93fb4..eea52da 100644 --- a/sw/SummerLoader64/src/main.c +++ b/sw/bootloader/src/main.c @@ -7,16 +7,14 @@ int main(void) { init_interrupts(); sc64_enable_sdram(); - sc64_disable_flash(); cic_type_t cic_type = sc64_get_cic_type(); tv_type_t tv_type = sc64_get_tv_type(); + cart_header_t *cart_header = boot_load_cart_header(); + + // Try to guess CIC and TV type from ROM in SDRAM if no override is provided if (cic_type == E_CIC_TYPE_UNKNOWN || tv_type == E_TV_TYPE_UNKNOWN) { - // Try to guess CIC and TV type from ROM in SDRAM if no override is provided - - cart_header_t *cart_header = boot_load_cart_header(); - if (cic_type == E_CIC_TYPE_UNKNOWN) { cic_type = boot_get_cic_type(cart_header); } @@ -28,5 +26,5 @@ int main(void) { disable_interrupts(); - boot(cic_type, tv_type); + boot(cart_header, cic_type, tv_type); } diff --git a/sw/SummerLoader64/src/n64_regs.h b/sw/bootloader/src/n64_regs.h similarity index 100% rename from sw/SummerLoader64/src/n64_regs.h rename to sw/bootloader/src/n64_regs.h diff --git a/sw/SummerLoader64/src/sc64.c b/sw/bootloader/src/sc64.c similarity index 82% rename from sw/SummerLoader64/src/sc64.c rename to sw/bootloader/src/sc64.c index 8fea3e9..fa8a310 100644 --- a/sw/SummerLoader64/src/sc64.c +++ b/sw/bootloader/src/sc64.c @@ -28,14 +28,6 @@ void sc64_disable_sdram(void) { sc64_disable_peripheral(SC64_CONFIG_SDRAM_ENABLE); } -void sc64_enable_flash(void) { - sc64_enable_peripheral(SC64_CONFIG_FLASH_ENABLE); -} - -void sc64_disable_flash(void) { - sc64_disable_peripheral(SC64_CONFIG_FLASH_ENABLE); -} - cic_type_t sc64_get_cic_type(void) { uint32_t boot = io_read(SC64_BOOT_REG); diff --git a/sw/SummerLoader64/src/sc64.h b/sw/bootloader/src/sc64.h similarity index 72% rename from sw/SummerLoader64/src/sc64.h rename to sw/bootloader/src/sc64.h index 1206276..ff8983d 100644 --- a/sw/SummerLoader64/src/sc64.h +++ b/sw/bootloader/src/sc64.h @@ -3,8 +3,6 @@ #include "boot.h" -void sc64_enable_flash(void); -void sc64_disable_flash(void); void sc64_enable_sdram(void); void sc64_disable_sdram(void); cic_type_t sc64_get_cic_type(void); diff --git a/sw/SummerLoader64/src/sc64_regs.h b/sw/bootloader/src/sc64_regs.h similarity index 79% rename from sw/SummerLoader64/src/sc64_regs.h rename to sw/bootloader/src/sc64_regs.h index 0ce10b7..5e5cd1d 100644 --- a/sw/SummerLoader64/src/sc64_regs.h +++ b/sw/bootloader/src/sc64_regs.h @@ -5,7 +5,6 @@ #define SC64_CONFIG_REG (0x1E000000) -#define SC64_CONFIG_FLASH_ENABLE (1 << 0) #define SC64_CONFIG_SDRAM_ENABLE (1 << 1) // Boot CIC and TV type override register @@ -19,8 +18,4 @@ #define SC64_BOOT_TV_TYPE_BIT (4) #define SC64_BOOT_TV_TYPE(x) (((x) & SC64_BOOT_TV_TYPE_MSK) >> SC64_BOOT_TV_TYPE_BIT) -// Flash arbitrary command register - -#define SC64_FLASH_CFG_REG (0x1C000000) - #endif diff --git a/sw/cic b/sw/cic new file mode 160000 index 0000000..16fd1ee --- /dev/null +++ b/sw/cic @@ -0,0 +1 @@ +Subproject commit 16fd1eecd8528ab2d7fd0490f24fb7738017bd39