From 3addf412d68b02560c6b4bbdb405a6f436a6e57e Mon Sep 17 00:00:00 2001 From: maksyuki Date: Sat, 22 Aug 2026 19:47:40 +0800 Subject: [PATCH] refactor: refactor RTL source paths in style audit and test files - Added i2c_if.sv and spi_if.sv to rtl_style_audit.json for proper inclusion in style checks. - Updated test_i2c.py to reference i2c_if.sv from the serial directory instead of the cluster IP directory. - Modified test_spisd.py to point to spi_if.sv in the storage directory instead of the cluster IP directory. --- .github/workflows/_regression.yml | 5 - .github/workflows/release.yml | 5 - ATTRIBUTIONS.md | 11 +- NOTICE | 3 + dependencies/dependencies.lock.json | 5 - rtl/README.md | 5 +- rtl/ip/serial/i2c_if.sv | 33 + rtl/ip/storage/spi_if.sv | 23 + rtl/mini/filelist/clusterip.fl | 2 - rtl/mini/filelist/inc.fl | 2 - rtl/mini/filelist/ip.fl | 2 + rtl/mini/filelist/tb.fl | 6 +- rtl/mini/formal/generate_formal_filelist.py | 2 +- rtl/mini/lint.msg | 2 +- rtl/mini/mk/formal.mk | 2 +- rtl/mini/script/generate_filelist.py | 1 + rtl/model/README.md | 16 + rtl/model/at24cxxx.sv | 2643 +++++++++++++++++ rtl/model/mic.sv | 38 + rtl/model/w25q128jvxim.sv | 2959 +++++++++++++++++++ rtl/rtl_style_audit.json | 2 + tests/test_i2c.py | 3 +- tests/test_spisd.py | 3 +- 23 files changed, 5741 insertions(+), 32 deletions(-) create mode 100644 rtl/ip/serial/i2c_if.sv create mode 100644 rtl/ip/storage/spi_if.sv create mode 100644 rtl/model/README.md create mode 100644 rtl/model/at24cxxx.sv create mode 100644 rtl/model/mic.sv create mode 100644 rtl/model/w25q128jvxim.sv diff --git a/.github/workflows/_regression.yml b/.github/workflows/_regression.yml index bc12b60e..8ace651b 100644 --- a/.github/workflows/_regression.yml +++ b/.github/workflows/_regression.yml @@ -65,17 +65,12 @@ jobs: physical/pdk/icsprout55-pdk physical/pdk/skywater-pdk rtl/managed/clusterip/archinfo - rtl/managed/clusterip/clint rtl/managed/clusterip/common rtl/managed/clusterip/crc - rtl/managed/clusterip/i2c - rtl/managed/clusterip/i2s - rtl/managed/clusterip/plic rtl/managed/clusterip/ps2 rtl/managed/clusterip/pwm rtl/managed/clusterip/rng rtl/managed/clusterip/rtc - rtl/managed/clusterip/spi rtl/managed/clusterip/wdg rtl/managed/third_party rtl/managed/mpw diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index db5f0fab..78306082 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,17 +24,12 @@ jobs: app/fatfs/ff16 physical/pdk/IHP-Open-PDK rtl/managed/clusterip/archinfo - rtl/managed/clusterip/clint rtl/managed/clusterip/common rtl/managed/clusterip/crc - rtl/managed/clusterip/i2c - rtl/managed/clusterip/i2s - rtl/managed/clusterip/plic rtl/managed/clusterip/ps2 rtl/managed/clusterip/pwm rtl/managed/clusterip/rng rtl/managed/clusterip/rtc - rtl/managed/clusterip/spi rtl/managed/clusterip/wdg rtl/managed/third_party rtl/managed/mpw diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md index cda67d31..a357adbc 100644 --- a/ATTRIBUTIONS.md +++ b/ATTRIBUTIONS.md @@ -38,6 +38,15 @@ Several SPI SD card related RTL files retain the original attribution to EmbedFi These headers state that the original code was published on GitHub by EmbedFire, but that the original license was not specified in the retained header. -## 5. Notes on redistribution +## 5. Device simulation models + +The simulation-only models under `rtl/model/` retain their upstream notices +and source formatting. The AT24 model is imported from the retroSoC I2C +repository, the microphone model from the retroSoC I2S repository, and the +W25Q128JVxIM model from the retroSoC SPI repository. The Winbond model retains +its original Winbond Electronics Corporation copyright and "All Rights +Reserved" notice; its inclusion does not relicense it under Mulan PSL v2. + +## 6. Notes on redistribution The preserved third-party notices are included to maintain attribution for code that was adapted or reused in this repository. They do not replace the repository's main license declaration. The retroSoC project itself is distributed under the Mulan PSL v2 license. diff --git a/NOTICE b/NOTICE index 0a6be07f..1edf2e29 100644 --- a/NOTICE +++ b/NOTICE @@ -8,5 +8,8 @@ The following third-party projects or authors are referenced in the RTL source h - Ravenna / PicoRV32 in ASIC: notices are preserved in rtl/ip/peripheral/gpio.sv, rtl/ip/peripheral/timer.sv, and rtl/ip/serial/uart.sv. - Hirosh Dabui: the original copyright notice is preserved in rtl/ip/memory/sdram_core.sv. - EmbedFire: attribution is preserved in the SPI SD card related files under rtl/ip/storage/. +- Device simulation models: the models under rtl/model/ preserve their upstream + notices, including the Winbond Electronics Corporation notice in the + W25Q128JVxIM model. These notices are included solely to preserve attribution for the retained third-party source material. They do not alter the licensing terms of the retroSoC repository itself, which is distributed under the Mulan PSL v2 license. diff --git a/dependencies/dependencies.lock.json b/dependencies/dependencies.lock.json index 89302e47..8abb5e24 100644 --- a/dependencies/dependencies.lock.json +++ b/dependencies/dependencies.lock.json @@ -61,17 +61,12 @@ "license": "Apache-2.0" }, "cluster_archinfo": {"url": "https://github.com/retroSoC/archinfo.git", "revision": "78505969a0afdbc887cdd9b36eda228b4afb616e", "destination": "rtl/managed/clusterip/archinfo", "license": "MulanPSL-2.0"}, - "cluster_clint": {"url": "https://github.com/retroSoC/clint.git", "revision": "ce49f1a5501c0a9f18c0e3f358417648f3f2fb18", "destination": "rtl/managed/clusterip/clint", "license": "NOASSERTION"}, "cluster_common": {"url": "https://github.com/retroSoC/common.git", "revision": "5604bd5899bcc3a937cef55db4bac3b5102e562c", "destination": "rtl/managed/clusterip/common", "license": "NOASSERTION"}, "cluster_crc": {"url": "https://github.com/retroSoC/crc.git", "revision": "8c1a5664c4d23d005c1afad432372a13ca967cc8", "destination": "rtl/managed/clusterip/crc", "license": "MulanPSL-2.0"}, - "cluster_i2c": {"url": "https://github.com/retroSoC/i2c.git", "revision": "73b024547c5cd70fb8bf801cc7757db2536c604f", "destination": "rtl/managed/clusterip/i2c", "license": "NOASSERTION"}, - "cluster_i2s": {"url": "https://github.com/retroSoC/i2s.git", "revision": "bd0978e8b45e4cf4c1335a1fa7311385d2a4e805", "destination": "rtl/managed/clusterip/i2s", "license": "NOASSERTION"}, - "cluster_plic": {"url": "https://github.com/retroSoC/plic.git", "revision": "c851d93829d2e75b224003a058e718925855c0cc", "destination": "rtl/managed/clusterip/plic", "license": "NOASSERTION"}, "cluster_ps2": {"url": "https://github.com/retroSoC/ps2.git", "revision": "2097a0d41b07573ff0453d65ba9a1670b3df7b9c", "destination": "rtl/managed/clusterip/ps2", "license": "MulanPSL-2.0"}, "cluster_pwm": {"url": "https://github.com/retroSoC/pwm.git", "revision": "79ae32f725e848a1e7f6134847b3fb032ed64006", "destination": "rtl/managed/clusterip/pwm", "license": "MulanPSL-2.0"}, "cluster_rng": {"url": "https://github.com/retroSoC/rng.git", "revision": "36f3e0774d8f3c4b597d3db8a96ca3992532b61e", "destination": "rtl/managed/clusterip/rng", "license": "MulanPSL-2.0"}, "cluster_rtc": {"url": "https://github.com/retroSoC/rtc.git", "revision": "32548c5cbcdf39e09d0880fb611f9dd47940ea88", "destination": "rtl/managed/clusterip/rtc", "license": "NOASSERTION"}, - "cluster_spi": {"url": "https://github.com/retroSoC/spi.git", "revision": "515cb408806584525605b4747db5e890dbdf1928", "destination": "rtl/managed/clusterip/spi", "license": "NOASSERTION"}, "cluster_wdg": {"url": "https://github.com/retroSoC/wdg.git", "revision": "f5d39fa246054d678d34195824d943367dfcce44", "destination": "rtl/managed/clusterip/wdg", "license": "MulanPSL-2.0"} }, "archives": { diff --git a/rtl/README.md b/rtl/README.md index f9b38ba0..003b850c 100644 --- a/rtl/README.md +++ b/rtl/README.md @@ -5,8 +5,9 @@ peripheral and technology wrappers, filelists, testbench support, and the Mini SoC build entry points. `mini/` is the active SoC integration flow. `managed/` contains locked or -vendored integration inputs, while `ip/` contains self-owned IP and -experiments. `filelist/` selects PDK-specific RTL sources; `tech/` contains +vendored integration inputs, `ip/` contains self-owned IP and experiments, and +`model/` contains committed device simulation models with preserved upstream +notices. `filelist/` selects PDK-specific RTL sources; `tech/` contains technology wrappers. Respect managed upstream boundaries and use setup helpers rather than editing generated MPW output. diff --git a/rtl/ip/serial/i2c_if.sv b/rtl/ip/serial/i2c_if.sv new file mode 100644 index 00000000..6a923bf1 --- /dev/null +++ b/rtl/ip/serial/i2c_if.sv @@ -0,0 +1,33 @@ +// Copyright (c) 2026 Yuchi Miao +// retroSoC is licensed under Mulan PSL v2. +// See LICENSE for the complete license text. + +interface i2c_if (); + logic scl_i; + logic scl_o; + logic scl_oe_o; + logic sda_i; + logic sda_o; + logic sda_oe_o; + logic irq_o; + + modport dut( + input scl_i, + output scl_o, + output scl_oe_o, + input sda_i, + output sda_o, + output sda_oe_o, + output irq_o + ); + + modport tb( + output scl_i, + input scl_o, + input scl_oe_o, + output sda_i, + input sda_o, + input sda_oe_o, + input irq_o + ); +endinterface diff --git a/rtl/ip/storage/spi_if.sv b/rtl/ip/storage/spi_if.sv new file mode 100644 index 00000000..8d4308eb --- /dev/null +++ b/rtl/ip/storage/spi_if.sv @@ -0,0 +1,23 @@ +// Copyright (c) 2026 Yuchi Miao +// retroSoC is licensed under Mulan PSL v2. +// See LICENSE for the complete license text. + +interface spi_if (); + logic sck_o; + logic nss_o; + logic mosi_o; + logic miso_i; + logic irq_o; + + modport dut(output sck_o, output nss_o, output mosi_o, input miso_i, output irq_o); + + // verilog_format: off -- preserve the signal-per-line testbench modport layout + modport tb( + input sck_o, + input nss_o, + input mosi_o, + output miso_i, + input irq_o + ); + // verilog_format: on +endinterface diff --git a/rtl/mini/filelist/clusterip.fl b/rtl/mini/filelist/clusterip.fl index e081f1f6..a83afab4 100644 --- a/rtl/mini/filelist/clusterip.fl +++ b/rtl/mini/filelist/clusterip.fl @@ -22,8 +22,6 @@ /clusterip/ps2/rtl/ps2_reg.sv /clusterip/ps2/rtl/ps2_if.sv /clusterip/ps2/rtl/apb4_ps2.sv -/clusterip/i2c/rtl/i2c_if.sv -/clusterip/spi/rtl/spi_if.sv /clusterip/rtc/rtl/rtc_pkg.sv /clusterip/rtc/rtl/rtc_if.sv /clusterip/rtc/rtl/rtc_tickgen.sv diff --git a/rtl/mini/filelist/inc.fl b/rtl/mini/filelist/inc.fl index f7d2150e..429beb1c 100644 --- a/rtl/mini/filelist/inc.fl +++ b/rtl/mini/filelist/inc.fl @@ -9,8 +9,6 @@ +incdir+/clusterip/rng/rtl +incdir+/clusterip/pwm/rtl +incdir+/clusterip/ps2/rtl -+incdir+/clusterip/i2c/rtl -+incdir+/clusterip/spi/rtl +incdir+/clusterip/rtc/rtl +incdir+/clusterip/wdg/rtl +incdir+/clusterip/crc/rtl diff --git a/rtl/mini/filelist/ip.fl b/rtl/mini/filelist/ip.fl index 585b9329..63a96188 100644 --- a/rtl/mini/filelist/ip.fl +++ b/rtl/mini/filelist/ip.fl @@ -14,6 +14,7 @@ /ip/memory/opipsram_reg.sv /ip/memory/opipsram_if.sv /ip/memory/apb4_opipsram.sv +/ip/storage/spi_if.sv /ip/storage/spisd_pkg.sv /ip/storage/spisd_define.svh /ip/storage/spisd_clock.sv @@ -31,6 +32,7 @@ /ip/storage/sdio_command.sv /ip/storage/sdio_data.sv /ip/storage/sdio_dma_descriptor.sv +/ip/serial/i2c_if.sv /ip/serial/i2c_filter.sv /ip/serial/i2c_core.sv /ip/serial/i2c_reg.sv diff --git a/rtl/mini/filelist/tb.fl b/rtl/mini/filelist/tb.fl index 6d275d3f..fe254d66 100644 --- a/rtl/mini/filelist/tb.fl +++ b/rtl/mini/filelist/tb.fl @@ -1,8 +1,8 @@ /clusterip/ps2/dv/model/ps2_device_model.sv -/clusterip/i2c/model/at24cxxx.sv +/device_model/at24cxxx.sv /model/uart_model.sv -/clusterip/i2s/model/mic.sv -/clusterip/spi/model/w25q128jvxim.sv +/device_model/mic.sv +/device_model/w25q128jvxim.sv /model/ESP_PSRAM64H.sv /model/DVP_CAMERA.sv /third_party/model/sdram/sdr.v diff --git a/rtl/mini/formal/generate_formal_filelist.py b/rtl/mini/formal/generate_formal_filelist.py index 2da5e21a..f6b6480f 100644 --- a/rtl/mini/formal/generate_formal_filelist.py +++ b/rtl/mini/formal/generate_formal_filelist.py @@ -142,7 +142,7 @@ def source_files(target: str) -> list[Path]: COMMON_RTL / "utils/register.sv", COMMON_RTL / "utils/fifo.sv", COMMON_RTL / "cdc/cdc_sync.sv", - ROOT / "rtl/managed/clusterip/i2c/rtl/i2c_if.sv", + SERIAL / "i2c_if.sv", SERIAL / "i2c_filter.sv", SERIAL / "i2c_core.sv", SERIAL / "i2c_reg.sv", diff --git a/rtl/mini/lint.msg b/rtl/mini/lint.msg index 6ac77bb4..daece616 100644 --- a/rtl/mini/lint.msg +++ b/rtl/mini/lint.msg @@ -1,5 +1,5 @@ { +lint=all; - -file=/hazard3/arith/hazard3_muldiv_seq.v,/hazard3/hazard3_frontend.v,/hazard3/arith/hazard3_onehot_encode.v,/hazard3/hazard3_instr_decompress.v,/physical/pdk/icsprout55-pdk/IP/IO/ICsprout_55LLULP1233_IO_251013/verilog/icsIOA_N55_3P3.v,/physical/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CR/verilog/ics55_LLSC_H7CR.v,/physical/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CL/verilog/ics55_LLSC_H7CL.v,/physical/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CH/verilog/ics55_LLSC_H7CH.v,/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_stdcell.v,/clusterip/i2c/model/at24cxxx.sv,/clusterip/spi/model/w25q128jvxim.sv,/clusterip/common/rtl/cdc/cdc_2phase.sv,/clusterip/common/rtl/utils/register.sv,/model/uart_model.sv,/tb/retrosoc_tb.sv,/clusterip/i2s/model/mic.sv,/model/ESP_PSRAM64H.sv,/third_party/model/sdram/sdr.v; + -file=/hazard3/arith/hazard3_muldiv_seq.v,/hazard3/hazard3_frontend.v,/hazard3/arith/hazard3_onehot_encode.v,/hazard3/hazard3_instr_decompress.v,/physical/pdk/icsprout55-pdk/IP/IO/ICsprout_55LLULP1233_IO_251013/verilog/icsIOA_N55_3P3.v,/physical/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CR/verilog/ics55_LLSC_H7CR.v,/physical/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CL/verilog/ics55_LLSC_H7CL.v,/physical/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CH/verilog/ics55_LLSC_H7CH.v,/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_stdcell.v,/device_model/at24cxxx.sv,/device_model/w25q128jvxim.sv,/clusterip/common/rtl/cdc/cdc_2phase.sv,/clusterip/common/rtl/utils/register.sv,/model/uart_model.sv,/tb/retrosoc_tb.sv,/device_model/mic.sv,/model/ESP_PSRAM64H.sv,/third_party/model/sdram/sdr.v; } { +warn=all; } diff --git a/rtl/mini/mk/formal.mk b/rtl/mini/mk/formal.mk index 21216e08..420d8fee 100644 --- a/rtl/mini/mk/formal.mk +++ b/rtl/mini/mk/formal.mk @@ -98,7 +98,7 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \ $(ROOT_PATH)/rtl/ip/serial/i2s_define.svh \ $(ROOT_PATH)/rtl/ip/serial/i2s_pkg.sv \ $(ROOT_PATH)/rtl/ip/serial/i2s_reg.sv \ - $(ROOT_PATH)/rtl/managed/clusterip/i2c/rtl/i2c_if.sv \ + $(ROOT_PATH)/rtl/ip/serial/i2c_if.sv \ $(ROOT_PATH)/rtl/ip/peripheral/timer_core.sv \ $(ROOT_PATH)/rtl/ip/peripheral/timer_define.svh \ $(ROOT_PATH)/rtl/ip/peripheral/timer_reg.sv \ diff --git a/rtl/mini/script/generate_filelist.py b/rtl/mini/script/generate_filelist.py index 522079a6..ae3a2004 100644 --- a/rtl/mini/script/generate_filelist.py +++ b/rtl/mini/script/generate_filelist.py @@ -26,6 +26,7 @@ PATH_PREFIXES = { "hazard3": RTL_DIR / "managed" / "hazard3" / "hdl", "mpw": RTL_DIR / "managed" / "mpw", "tb": MINI_DIR / "dv" / "tb", + "device_model": RTL_DIR / "model", "model": MINI_DIR / "dv" / "model", "sva": MINI_DIR / "dv" / "sva", "top": MINI_DIR / "top", diff --git a/rtl/model/README.md b/rtl/model/README.md new file mode 100644 index 00000000..e3ce908b --- /dev/null +++ b/rtl/model/README.md @@ -0,0 +1,16 @@ +# Device Simulation Models + +This directory contains committed device models used only by retroSoC simulation. +The models are excluded from the self-owned synthesizable RTL style profile and +retain their upstream notices and behavior. The repository import normalizes +line endings and trailing whitespace. + +| File | Upstream source | Revision | Upstream SHA-256 | +| --- | --- | --- | --- | +| `at24cxxx.sv` | `https://github.com/retroSoC/i2c.git` | `73b024547c5cd70fb8bf801cc7757db2536c604f` | `636ed7e58574735a80943122178da8e1b23a48d443ec11d96ccdbd8618da404b` | +| `mic.sv` | `https://github.com/retroSoC/i2s.git` | `bd0978e8b45e4cf4c1335a1fa7311385d2a4e805` | `2d5264092bac13f28c5e38cfba4a9288afb6d704c7821f40265177c32715a6b3` | +| `w25q128jvxim.sv` | `https://github.com/retroSoC/spi.git` | `515cb408806584525605b4747db5e890dbdf1928` | `4b95f007a39854dc2690cf0b608b1b4b56a61adbf1e7c736e7ab5d44d46a112f` | + +The Winbond model retains its upstream copyright and "All Rights Reserved" +notice. Inclusion here records provenance and does not relicense that model +under the retroSoC project license. diff --git a/rtl/model/at24cxxx.sv b/rtl/model/at24cxxx.sv new file mode 100644 index 00000000..1d71b486 --- /dev/null +++ b/rtl/model/at24cxxx.sv @@ -0,0 +1,2643 @@ +`timescale 1ns / 10 ps + + +// AT24C Device Family +// Note: Compiler directives are used to adapt the model to a particular +// device of the family. +// Note: Choose VERBOSE 0 to suppress troubleshooting messages. + +// Dates of Revisions: + +// m.d. ciletti 03/05/03 +// m.d. ciletti 04/28/2006 +// m.d. ciletti 05/30/2008 +// B. Morgan 7/23/2008 +// m.d. ciletti 7/26/2008 +// B. Schoendube 9/19/2014 +// C. Tomlinson 02/17/2020 AT24C64C 1.8V to 5.5V 400kHz max + +// Summary of Revisions +// Pre-2008 Modified to handle the following device functionality: + +// (1) Detects toggling of SDA under SCL = 1 and cancels the START condition +// if the sequence ends with a STOP condition. + +// (2) Detects and recovers from a random START condition, i.e. if a start +// condition appears anywhere in the data transmission the machine resets +// itself to begin receiving a device address byte. + +// (3) Detects a sequence of 9 clocks with SDA held high and resets the +// machine to await a START condition. The address register of the machine +// is not affected by the reset operation. + +// (4) Corrects an occurrence of an incorrect device label for the +// AT24C1024 device. + +// (5) Includes address code for the P bits (had been removed for testing) + +// 05/30/2008 Modified to deal with infinite loop upon time out for page +// write. + + +// 05/30/2008 Modified to provide additional documentation. + +// 7/23/2008 Added 24C512B 1.8v 400khz and 2.5v 1Mhz timing sets. + +// 7/26/2008 Modified to handle protocol violation with WP (write protection) +// and provide additional documentation. +// Required reset of Valid_Address_flag. +// Corrected code entries for AT24C512B. + +// 9/19/2014 added device types AT24C64E and AT24C32E +// Corrected possible bus access error when not selected +// Corrected tSUSTO timing checks +// Change timing callouts to be consistent +// Added timing checks for AT24C32E and AT24C64E + +// AT24C64E was not developed, removing AT24C64E references C.Tomlinson 01/08/2020 + +//////////////////////////////////////////////////////////////////////////// +// DEVICE SELECTION +//////////////////////////////////////////////////////////////////////////// + +`define WANT_VERBOSE 0 + +// Slave address formats +// 1M 1010_0_A1_P0_R/W +// 512K 1010_0_A1_A0_R/W +// 256K 1010_0_A1_A0_R/W +// 128K 1010_0_A1_A0_R/W +// 64K 1010_A2_A1_A0_R/W +// 32K 1010_A2_A2_A0_R/W +// 16K 1010_P2_P1_P0_R/W +// 8K 1010_A2_P1_P0_R/W +// 4K 1010_A2_A1_P0_R/W +// 2K 1010_A2_A1_A0_R/W +// 1K 1010_A2_A1_A0_R/W + +// To configure the model, edit the comments of the compiler +// directives as specfifed below : + +// (1) remove // from the `define line of the selected device (REQUIRED) +// (2) remove // to select timing parameters (OPTIONAL) +// (3) set the SLAVE_ADDRESS (REQUIRED) +// (Arbitrary addresses are used below for testing) +// Be sure that the test bench is adapted to the particular model +// No further action needed. + + +// `define AT24C64D + +//`define AT24C1024 +//`define AT24C512B +// define AT24C512 +//`define AT24C256 +//`define AT24C128 + +//`define AT24C64A +//`define AT24C32A +//`define AT24C16A +//`define AT24C08A +`define AT24C04A +//`define AT24C02A +//`define AT24C01A + +//`define AT24C64 +//`define AT24C32 +//`define AT24C08 +// `define AT24C04 +// `define AT24C02 + +//`define AT34C02 + +// Options for timing parameters + +//`define WANT_AT24C1024__4_5__5_5V_TIMING + +//using existing 1024 400kHz timing set for AT24C32E C.Tomlinson 01/08/2020 +// `define WANT_AT24C1024__2_7__5_5V_TIMING + +//`define WANT_AT24C_01A_02_04_08_16__2_7__2_5__1_8_V_TIMING +// `define WANT_AT24C_01A_02_04_08_16__5_V_TIMING + +// `define WANT_AT24C_02A_04A_08A_16A__1_8_V_TIMING +// `define WANT_AT24C_02A_04A_08A__2_5V__2_7V_TIMING +//`define WANT_AT24C_16A__2_5V_TIMING +`define WANT_AT24C_02A_04A_08A_16A__5V_TIMING + +//`define WANT_AT24C512B__1_8V_TIMING +//`define WANT_AT24C512B__2_5V_TIMING +//`define WANT_AT24C512__1_8_V_TIMING +//`define WANT_AT24C512__2_7V_TIMING +//`define WANT_AT24C512__5V_TIMING + +//`define WANT_AT24C128_256__1_8_V_TIMING +//`define WANT_AT24C128_256__2_5V_TIMING +//`define WANT_AT24C128_256__5V_TIMING +//`define WANT_AT34C02__1_8_V_TIMING +//`define WANT_AT34C02__2_7__5_0_V_TIMING + +`ifdef AT24C1024 +module AT24C1024 (SDA, SCL, WP); +`endif +`ifdef AT24C512B + +module AT24C512B (SDA, SCL, WP); +`endif +`ifdef AT24C512 + +module AT24C512 (SDA, SCL, WP); +`endif +`ifdef AT24C256 + +module AT24C256 (SDA, SCL, WP); +`endif +`ifdef AT24C128 + +module AT24C128 (SDA, SCL, WP); +`endif +`ifdef AT24C64B + +module AT24C64B (SDA, SCL, WP); //cjt 10_25_2021 added B +`endif +`ifdef AT24C64D + +module AT24C64D (SDA, SCL, WP); //cjt 10_25_2021 added D +`endif +`ifdef AT24C32D + +module AT24C32 (SDA, SCL, WP); +`endif +`ifdef AT24C64A + +module AT24C64 (SDA, SCL, WP); +`endif +`ifdef AT24C32A + +module AT24C32 (SDA, SCL, WP); +`endif +`ifdef AT24C16A + +module AT24C16A (SDA, SCL, WP); +`endif +`ifdef AT24C08A + +module AT24C08A (SDA, SCL, WP); +`endif +`ifdef AT24C04A + +module AT24C04 (SDA, SCL, WP); +`endif +`ifdef AT24C02A + +module AT24C02A (SDA, SCL, WP); +`endif +`ifdef AT24C01A + +module AT24C01A (SDA, SCL, WP); +`endif +`ifdef AT24C64 + +module AT24C64 (SDA, SCL, WP); +`endif +`ifdef AT24C32 + +module AT24C32 (SDA, SCL, WP); +`endif +`ifdef AT24C16 + +module AT24C16 (SDA, SCL, WP); +`endif +`ifdef AT24C08 + +module AT24C08 (SDA, SCL, WP); +`endif +`ifdef AT24C04 + +module AT24C04 (SDA, SCL, WP); +`endif +`ifdef AT24C02 + +module AT24C02 (SDA, SCL, WP); +`endif +`ifdef AT34C02 + +module AT34C02 (SDA, SCL, WP); +`endif + +inout SDA; // Bi-directional serial data +input SCL; // Serial clock +input WP; // Write protection + + + +///////////////////////////////////////////////////////////////////////////// +////////////////////////////// DEVICE CONFIGURATION /////////////////////// +// Slave address formats +// 1M 1010_0_A1_P0_R/W +// 512K 1010_0_A1_A0_R/W +// 256K 1010_0_A1_A0_R/W +// 128K 1010_0_A1_A0_R/W +// 64K 1010_A2_A1_A0_R/W +// 32K 1010_A2_A2_A0_R/W +// 16K 1010_P1_P1_P0_R/W +// 8K 1010_A2_P1_P0_R/W +// 4K 1010_A2_A1_P0_R/W +// 2K 1010_A2_A1_A0_R/W +// 1K 1010_A2_A1_A0_R/W + +// The following parameters configure the device. + +// The A2_A1_A0 of the slave addresses are arbitrary for the +// purpose of testing the model. A testbench must send the indicated +// device address, together with the appropriate memory address bits +// P2_P1_P0, as needed. + +//AT24C1024, 512 pages at 256 bytes/page, 17 address bits +`ifdef AT24C1024 + +parameter MEM_SIZE = 131072; +parameter ADDR_SIZE = 17; +parameter WORD_ADDR_SIZE = 8; +parameter SLAVE_ADDRESS = 6'b1010_00; // Testbench provides P0 and R/W bits +`endif + +// AT24C512B, 512 pages at 128 bytes/page, 16 address bits +`ifdef AT24C512B + +parameter MEM_SIZE = 65536; +parameter ADDR_SIZE = 16; +parameter WORD_ADDR_SIZE = 7; +parameter SLAVE_ADDRESS = 7'b1010_011; // Testbench provides R/W bit +`endif + + +// AT24C512, 512 pages at 128 bytes/page, 16 address bits +`ifdef AT24C512 + +parameter MEM_SIZE = 65536; +parameter ADDR_SIZE = 16; +parameter WORD_ADDR_SIZE = 7; +parameter SLAVE_ADDRESS = 7'b1010_011; // Testbench provides R/W bit +`endif + +// AT24C256, 512 pages at 64 bytes/page, 15 address bits +`ifdef AT24C256 + +parameter MEM_SIZE = 32768; +parameter ADDR_SIZE = 15; +parameter WORD_ADDR_SIZE = 6; +parameter SLAVE_ADDRESS = 7'b1010_011; // Testbench provides R/W bit +`endif + +// AT24C128, 256 pages at 64 bytes/page, 14 address bits +`ifdef AT24C128 + +parameter MEM_SIZE = 16384; +parameter ADDR_SIZE = 14; +parameter WORD_ADDR_SIZE = 6; +parameter SLAVE_ADDRESS = 7'b1010_011; // Testbench provides R/W bit +`endif + +// AT24C64, 256 pages at 32 bytes/page, 13 address bits +`ifdef AT24C64 + +parameter MEM_SIZE = 8192; +parameter ADDR_SIZE = 13; +parameter WORD_ADDR_SIZE = 5; +parameter SLAVE_ADDRESS = 7'b1010_111; // Testbench provides R/W bit +`endif + +// AT24C64A, 256 pages at 32 bytes/page, 13 address bits +`ifdef AT24C64A + +parameter MEM_SIZE = 8192; +parameter ADDR_SIZE = 13; +parameter WORD_ADDR_SIZE = 5; +parameter SLAVE_ADDRESS = 7'b1010_111; // Testbench provides R/W bit +`endif + +// AT24C64B, 256 pages at 32 bytes/page, 13 address bits +`ifdef AT24C64B + +parameter MEM_SIZE = 8192; +parameter ADDR_SIZE = 13; +parameter WORD_ADDR_SIZE = 5; +parameter SLAVE_ADDRESS = 7'b1010_111; // Testbench provides R/W bit +`endif + +// AT24C64C, 256 pages at 32 bytes/page, 13 address bits +`ifdef AT24C64C + +parameter MEM_SIZE = 8192; +parameter ADDR_SIZE = 13; +parameter WORD_ADDR_SIZE = 5; +parameter SLAVE_ADDRESS = 7'b1010_111; // Testbench provides R/W bit +`endif + +// AT24C64D, 256 pages at 32 bytes/page, 13 address bits +`ifdef AT24C64D + +parameter MEM_SIZE = 8192; +parameter ADDR_SIZE = 13; +parameter WORD_ADDR_SIZE = 5; +parameter SLAVE_ADDRESS = 7'b1010_111; // Testbench provides R/W bit +`endif + +// AT24C32, 128 pages at 32 bytes/page, 12 address bits +`ifdef AT24C32 + +parameter MEM_SIZE = 4096; +parameter ADDR_SIZE = 12; +parameter WORD_ADDR_SIZE = 5; +parameter SLAVE_ADDRESS = 7'b1010_101; // Testbench provides R/W bit +`endif + +// AT24C32D, 128 pages at 32 bytes/page, 12 address bits +`ifdef AT24C32D + +parameter MEM_SIZE = 4096; +parameter ADDR_SIZE = 12; +parameter WORD_ADDR_SIZE = 5; +parameter SLAVE_ADDRESS = 7'b1010_101; // Testbench provides R/W bit +`endif + +// AT24C32A, 128 pages at 32 bytes/page, 12 address bits +`ifdef AT24C32A + +parameter MEM_SIZE = 4096; +parameter ADDR_SIZE = 12; +parameter WORD_ADDR_SIZE = 5; +parameter SLAVE_ADDRESS = 7'b1010_101; // Testbench provides R/W bit +`endif + +// AT24C16, 128 pages at 16 bytes/page, 11 address bits +`ifdef AT24C16 + +parameter MEM_SIZE = 2048; +parameter ADDR_SIZE = 11; +parameter WORD_ADDR_SIZE = 4; +parameter SLAVE_ADDRESS = 4'b1010; // Testbench provides P2_P1_P0_R/W bits +`endif + +// AT24C16A, 128 pages at 16 bytes/page, 11 address bits +`ifdef AT24C16A + +parameter MEM_SIZE = 2048; +parameter ADDR_SIZE = 11; +parameter WORD_ADDR_SIZE = 4; +parameter SLAVE_ADDRESS = 4'b1010; // Testbench provides P2_P1_P0_R/W bits +`endif + +// AT24C08, 64 pages at 16 bytes/page, 10 address bits +`ifdef AT24C08 + +parameter MEM_SIZE = 1024; +parameter ADDR_SIZE = 10; +parameter WORD_ADDR_SIZE = 4; +parameter SLAVE_ADDRESS = 5'b1010_0; // Testbench provides P1_P0_R/W bits + +`endif +// AT24C08A, 64 pages at 16 bytes/page, 10 address bits +`ifdef AT24C08A + +parameter MEM_SIZE = 1024; +parameter ADDR_SIZE = 10; +parameter WORD_ADDR_SIZE = 4; +parameter SLAVE_ADDRESS = 5'b1010_0; // Testbench provides P1_P0_R/W bits +`endif + +// AT24C04, 32 pages at 16 bytes/page, 9 address bits +`ifdef AT24C04 + +parameter MEM_SIZE = 512; +parameter ADDR_SIZE = 9; +parameter WORD_ADDR_SIZE = 4; +parameter SLAVE_ADDRESS = 6'b1010_00; // Testbench provides P0_R/W bits +`endif + +// AT24C04A, 32 pages at 16 bytes/page, 9 address bits +`ifdef AT24C04A + +parameter MEM_SIZE = 512; +parameter ADDR_SIZE = 9; +parameter WORD_ADDR_SIZE = 4; +parameter SLAVE_ADDRESS = 6'b1010_00; // Testbench provides P0_R/W bits +`endif + +// AT24C02, 32 pages at 8 bytes/page, 8 address bits +`ifdef AT24C02 + +parameter MEM_SIZE = 256; +parameter ADDR_SIZE = 8; +parameter WORD_ADDR_SIZE = 3; +parameter SLAVE_ADDRESS = 7'b1010_100; // Testbench provides R/W bit +`endif + +// AT24C02A, 32 pages at 8 bytes/page, 8 address bits +`ifdef AT24C02A + +parameter MEM_SIZE = 256; +parameter ADDR_SIZE = 8; +parameter WORD_ADDR_SIZE = 3; +parameter SLAVE_ADDRESS = 7'b1010_100; // Testbench provides R/W bit +`endif + +// AT24C01A, 16 page at 8 bytes/page, 7 address bits +`ifdef AT24C01A + +parameter MEM_SIZE = 128; +parameter ADDR_SIZE = 7; +parameter WORD_ADDR_SIZE = 3; +parameter SLAVE_ADDRESS = 7'b1010_110; // Testbench provides R/W bits +`endif + +// AT34C02, 16 pages at 16 bytes/page, 8 address bits +`ifdef AT34C02 + +parameter MEM_SIZE = 256; +parameter ADDR_SIZE = 8; +parameter WORD_ADDR_SIZE = 4; +parameter SLAVE_ADDRESS = 7'b1010_100; // Testbench provides R/W bit +`endif + +// Device Parameters for internal decoding + +// AT24C1024, 512 pages at 256 bytes/page, 17 address bits +parameter MEM_SIZE_1MEG_BIT = 131072; +parameter ADDR_SIZE_1M_BIT = 17; +parameter WORD_ADDR_SIZE_1M_BIT = 8; + +// AT24C512B, 512 pages at 128 bytes/page, 16 address bits +parameter MEM_SIZE_500K_BIT = 65536; +parameter ADDR_SIZE_500K_BIT = 16; +parameter WORD_ADDR_SIZE_500K_BIT = 7; + +// Removed to avoid redefinition error at compilation +// AT24C512, 512 pages at 128 bytes/page, 16 address bits +// parameter MEM_SIZE_500K_BIT = 65536; +// parameter ADDR_SIZE_500K_BIT = 16; +// parameter WORD_ADDR_SIZE_500K_BIT = 7; + +// AT24C256, 512 pages at 64 bytes, 15 address bits +parameter MEM_SIZE_256K_BIT = 32768; +parameter ADDR_SIZE_256K_BIT = 15; +parameter WORD_ADDR_SIZE_256K_BIT = 6; + +// AT24C128, 256 pages at 64 bytes, 14 address bits +parameter MEM_SIZE_128K_BIT = 16384; +parameter ADDR_SIZE_128K_BIT = 14; +parameter WORD_ADDR_SIZE_128K_BIT = 6; + +// AT24C64, AT24C64A, AT24C64B, AT24C64C, AT24C64D 256 pages at 32 bytes, 13 address bits +parameter MEM_SIZE_64K_BIT = 8192; +parameter ADDR_SIZE_64K_BIT = 13; +parameter WORD_ADDR_SIZE_64K_BIT = 5; + +// AT24C32, AT24C32A, AT24C32D 128 pages at 32 bytes/page, 12 address bits +parameter MEM_SIZE_32K_BIT = 4096; +parameter ADDR_SIZE_32K_BIT = 12; +parameter WORD_ADDR_SIZE_32K_BIT = 5; + +// AT24C16, AT24C16A, 128 pages at 16 bytes/page, 11 address bits +parameter MEM_SIZE_16K_BIT = 2048; +parameter ADDR_SIZE_16K_BIT = 11; +parameter WORD_ADDR_SIZE_16K_BIT = 4; + +// AT24C08, AT24C08A, 64 pages at 16 bytes/page, 10 address bits +parameter MEM_SIZE_8K_BIT = 1024; +parameter ADDR_SIZE_8K_BIT = 10; +parameter WORD_ADDR_SIZE_8K_BIT = 4; + +// AT24C04, AT24C04A, 32 pages at 16 bytes/page, 9 address bits + +parameter MEM_SIZE_4K_BIT = 512; + +parameter ADDR_SIZE_4K_BIT = 9; +parameter WORD_ADDR_SIZE_4K_BIT = 4; + +// AT24C02, AT24C02A 32 pages at 8 bytes/page, 8 address bits +parameter MEM_SIZE_2K_BIT = 256; +parameter ADDR_SIZE_2K_BIT = 8; +parameter WORD_ADDR_SIZE_2K_BIT = 3; + +// AT24C01A, 16 pages at 8 bytes/page, 7 ADDRESS bits +parameter MEM_SIZE_1K_BIT = 128; +parameter ADDR_SIZE_1K_BIT = 7; +parameter WORD_ADDR_SIZE_1K_BIT = 3; + +// AT34C02, 16 pages at 16 bytes/page, 8 address bits +//parameter MEM_SIZE_2K_BIT = 256; +//parameter ADDR_SIZE_2K_BIT = 8; +parameter WORD_ADDR_SIZE_AT34C02_2K_BIT = 4; + +parameter BYTE_SIZE = 8; + +// parameter POWER_UP_DELAY = 50000; // For testing CJT 06_22_2020 +parameter VERBOSE = `WANT_VERBOSE; // Enable $display statements +// parameter TIMEOUT_FOR_WRITE = 5_000_000; // 5 ms +parameter TIMEOUT_FOR_WRITE = 5_000; // For testing +// parameter TIMEOUT_FOR_WRITE = 0; // For testing +//*************************************************************************** +//*************************************************************************** +// Write Protection (Externally applied signal) +//*************************************************************************** +//*************************************************************************** + +// WP = 0 if not protected +// WP = 1 if protected + +//*************************************************************************** +//*************************************************************************** +// Registers and Memory +//*************************************************************************** +//*************************************************************************** + +reg [(ADDR_SIZE - 1):0] addr_reg ; // address register +reg [(BYTE_SIZE - 1):0] memory[(MEM_SIZE-1):0] ; //Device memory +reg [BYTE_SIZE-1: 0] S_Byte_Shft_Reg; +reg [6: 0] Device_Address; // Maximum of 7 bits +reg load_address_bit, M_ACK; + +reg ld_S_Byte_Shft_Reg; +reg ld_addr_reg_MSB_byte; +reg ld_addr_reg_LSB_byte; +reg shift_in; +reg shift_out; + +parameter SEND = 1, RECEIVE = 0; +parameter [31:0] SENDING = "send"; +parameter [31:0] RECEIVING = "recv"; +reg [2:0] state_recv, next_state_recv; +reg [2:0] state_send, next_state_send; +wire SDA_out; +reg S_send_rcvb; +reg Addr_Done; +reg S_ACK; // Active-high 3-state +// control of SDA_out + +reg Valid_Address_flag; // Detects valid device address from master +reg NO_ACK_flag; // Flag indicating No_ACK from master +reg Time_Out_flag; // Time out flag +reg Exec_Page_Write_flag; // Execute page write flag +reg Exec_Seq_Read_flag; // Execute sequential read flag +reg RUN_flag; // Asserts after power-up +reg Exec_Random_Read_flag; +reg Write_flag; +wire [BYTE_SIZE-1: 0] mem_byte = memory[addr_reg]; // For debug of READ +reg First_START_flag; +reg [8:0] Protocol_Recovery_reg; +reg Device_Address_Active; //Indicates when the device address field is active + +event STOP_condition; +event START_condition; +event START_trigger; +event incr_addr_reg; // increments the memory address +event incr_word_addr; // increments the page address +reg S_START; // Flag set on falling edge of SCL for +// a valid start condition. Cleared on +// the next rising edge of SCL +reg S_STOP; + +`ifdef AT34C02 + +reg AT34C02_SWP_reg; +reg AT34C02_SWP_enabled; +`endif + +//************************************************************************ +//************************// Slave bus controls ************************* +//************************************************************************ + +triand SDA_int = (S_send_rcvb == SEND)? + ((S_ACK == 0)? SDA_out: 1'b0): 1'bz; +tri SDA = RUN_flag ? SDA_int: 1'bz; +tri SDA_in = (S_send_rcvb == RECEIVE)? SDA : 1'bz; + +wire [31:0] receiver = (S_send_rcvb == SEND)? + SENDING : ((S_send_rcvb == RECEIVE)) ? + RECEIVING : 32'bx; // For ASCII display + +//************************************************************************ + +//************************ Memory Initialization ************************* +//************************************************************************ + +integer k; +initial + for (k = 0; k <= MEM_SIZE-1; k = k+1) + memory[k] = {BYTE_SIZE {1'b1}}; + +//************************************************************************ +//*************************** Address Register Controls ****************** +//************************************************************************ + +always @(posedge SCL) + if (ld_addr_reg_MSB_byte == 1) + begin +`ifdef AT24C1024 + addr_reg[ADDR_SIZE-2: 8] <= S_Byte_Shft_Reg; // Fixed 214C1024 + +`endif + `ifdef AT24C512 + + addr_reg[ADDR_SIZE-1: 8] <= S_Byte_Shft_Reg; +`endif + `ifdef AT24C512B // 7/26/2008 + + addr_reg[ADDR_SIZE-1: 8] <= S_Byte_Shft_Reg; +`endif + `ifdef AT24C256 + + addr_reg[ADDR_SIZE-1: 8] <= S_Byte_Shft_Reg; +`endif + `ifdef AT24C128 + + addr_reg[ADDR_SIZE-1: 8] <= S_Byte_Shft_Reg; +`endif + `ifdef AT24C64 + + addr_reg[ADDR_SIZE-1: 8] <= S_Byte_Shft_Reg; +`endif + `ifdef AT24C64A + + addr_reg[ADDR_SIZE-1: 8] <= S_Byte_Shft_Reg; +`endif + +`ifdef AT24C64B + + addr_reg[ADDR_SIZE-1: 8] <= S_Byte_Shft_Reg; +`endif + +`ifdef AT24C64C + + addr_reg[ADDR_SIZE-1: 8] <= S_Byte_Shft_Reg; +`endif + +`ifdef AT24C64D + + addr_reg[ADDR_SIZE-1: 8] <= S_Byte_Shft_Reg; +`endif + +`ifdef AT24C32 + + addr_reg[ADDR_SIZE-1: 8] <= S_Byte_Shft_Reg; +`endif + `ifdef AT24C32A + + addr_reg[ADDR_SIZE-1: 8] <= S_Byte_Shft_Reg; +`endif + `ifdef AT24C32E + + addr_reg[ADDR_SIZE-1: 8] <= S_Byte_Shft_Reg; +`endif + + end + else if (ld_addr_reg_LSB_byte == 1) + case (MEM_SIZE) + + + MEM_SIZE_1K_BIT: + addr_reg[6:0] <= S_Byte_Shft_Reg[6: 0]; + default: + addr_reg[7:0] <= S_Byte_Shft_Reg[7: 0]; + endcase + +always @(incr_word_addr) // Increment address within a page after WRITE + addr_reg[WORD_ADDR_SIZE -1: 0] <= addr_reg[WORD_ADDR_SIZE -1: 0] +1; + +always @(incr_addr_reg) // Increment address within mem space after READ + addr_reg <= addr_reg + 1; + +//************************************************************************* +//************************** Shift Register ************************ +//************************************************************************* + +assign SDA_out = S_Byte_Shft_Reg[BYTE_SIZE-1] ? 1'bz : 1'b0; + +always @(posedge SCL) + if ((shift_in == 1) && (!Time_Out_flag)) + S_Byte_Shft_Reg <= {S_Byte_Shft_Reg [BYTE_SIZE-2:0], SDA_in }; + +always @ (negedge SCL) + if (shift_out == 1) + S_Byte_Shft_Reg <= S_Byte_Shft_Reg << 1; + + else if (ld_S_Byte_Shft_Reg == 1) + S_Byte_Shft_Reg <= memory[addr_reg]; + + + +//************************************************************************* +//********************* Protocol Recovery Register ********************** +//************************************************************************* + +always @(posedge SCL) +begin // Simplified 7/26/2008 + if ({Protocol_Recovery_reg [7:0], SDA_in} == 9'b1_1111_1111) + begin + $display("Protocol Recovery initiated"); + Recover_from_Protocol_Interupt; + disable Data_Transmission.Outer_Block.Inner_Block; + + end +end + + +//************************************************************************ +//*********************** Read/Write State Machine *********************** +//************************************************************************ +//************************ Power-Up ************************* +//************************************************************************ + +initial +begin // Power-Up Initialization +`ifdef AT34C02 + AT34C02_SWP_reg = 0; // one-time programmable + AT34C02_SWP_enabled = 0; // one-time programmable +`endif + + Power_up_initialization; + if (VERBOSE) + $display("Begin Power Up Sequence"); + if (VERBOSE) + $display ("Waiting for START_condition"); + S_send_rcvb = RECEIVE; +end // Power-Up Initialization + +//************************************************************************ +//********************* S_START and S_STOP conditions ******************** +//************************************************************************ + +// Start trigger allows latency between start condition and falling edge +// of SCL. If stop occurs in this interval the machine ignores the start +// condition and awaits a subsequent start condition. + +// START Condition Detection + +always @ (negedge SDA_in) + if(SCL == 1) + begin + if (VERBOSE) + $display("START condition detected", $time); + S_STOP = 0; + S_START = 1; + -> START_condition; // Start condition event + + if (VERBOSE) + $display ("Got START condtion event"); + + @(STOP_condition or negedge SCL) + if (SCL == 0) + begin + if (VERBOSE) + $display ("First negedge of SCL after START", $time); + Valid_Address_flag = 0; + if(VERBOSE) + $display("START event triggered"); + -> START_trigger; // Start trigger event + @ (posedge SCL) S_START = 0; // Waits until cycle ends + end + else + begin // STOP condition detected under same clock + if (VERBOSE) + $display ("STOP under same clock as START"); + S_START = 0; + S_STOP = 1; + end + end + + + // STOP Condition Detection + + always @ (posedge SDA_in) // Stop condition + if(SCL == 1) + begin + // S_START = 0; 04/24/2006 + S_STOP = 1; + + if (VERBOSE) + $display ("STOP condition", $time); + -> STOP_condition; + Valid_Address_flag = 0; + ld_S_Byte_Shft_Reg = 0; + shift_in = 0; + shift_out = 0; + end + + + + //************************************************************************ + //*** Data_Tranmission: Response to START_trigger event ************ + //************************************************************************ + + // Note: START_trigger is synched to a drop in SCL after start condition + // to deal with possibility of multiple drops of SDA under the clock + // before the falling edge of the clock. (05/30/2008) + + + + always @ (START_trigger) + begin: Data_Transmission //04/24/2006 + First_START_flag = 1; + if (VERBOSE) + $display("******************* Data Transmission Triggered by start", $time); + + forever + begin: Outer_Block + + // Check for power up enabled + if (VERBOSE && (!RUN_flag)) + begin + $display ("MACHINE NOT POWERED UP"); + disable Data_Transmission; + end + + // Check for time-out condition + if (RUN_flag && (Time_Out_flag)) + begin + if (VERBOSE) + $display("Time_Out_flag Asserted - Skip START condition"); + if (VERBOSE) + $display($realtime); + disable Data_Transmission; // 05/30/3008 Prevent inf loop + //disable Outer_Block; // 05/30/3008 Prevent inf loop + end + + else + begin: Inner_Block + + Recover_from_Protocol_Interupt; // Clear flags and variables as needed + if (VERBOSE) + $display("Valid Start Condition - Begin Data_Transmission", $time); + if (VERBOSE) + $display("Getting Device Address Byte", $time); + + Get_a_Byte; // Reads 8 bits of data on (rising edge of SCL) + if(VERBOSE) + $display("Byte received: %H ",S_Byte_Shft_Reg, $time); + @ (negedge SCL) // First drop in SCL after byte is read + shift_in = 0; + NO_ACK_flag = 0; + if (Device_Address_Active) + Check_for_Valid_Upper_Bits_of_Device_Address; + if (Valid_Address_flag) + begin: Got_Address_Match + if (VERBOSE) + $display ("Device Address_Match Found", $time); + case (S_Byte_Shft_Reg[0]) // LSB = 0 for write, 1 for read + + 0: + Write_or_Dummy_Write_with_Random_Read; + 1: + Current_Address_and_Sequential_Read; + endcase + if (VERBOSE) + $display ("S_Byte_Shft_Reg[0] = ", S_Byte_Shft_Reg[0]); + end // Got_Address_Match + else if (VERBOSE) + $display("Invalid device address", $time); + + end // Inner_Block + end // Outer_Block + end // Data_Transmission + + //*********************** + //*** enable device address active + //************************* + always @ (START_trigger) + begin + Device_Address_Active = 1; // Only active during first byte after start. BS 09/19/2014 + repeat (8) + @ (posedge SCL); + + + @(negedge SCL) + #1 Device_Address_Active = 0; // Delay to insure signal persists after needed + end + + + // modified 04/24/2006 to detect stop under same clock as start and + // ignore the start condition. + + //************************************************************************ + //*** Data_Tranmission: Response to STOP condition ************ + //************************************************************************ + + always @ (STOP_condition) + begin: STOP_Block + if (S_START ==1) + begin + S_START = 0; + S_STOP = 1; + disable STOP_Block; + end + else + begin + + if (VERBOSE) + $display ("****************** SLAVE STOP condition detected"); + + S_START = 0; + S_STOP = 1; + Valid_Address_flag = 0; + Exec_Seq_Read_flag = 0; + Exec_Random_Read_flag = 0; + +`ifdef AT34C02 // Special tratment for AT34C02 + + if ((AT34C02_SWP_enabled == 0) && (AT34C02_SWP_reg == 1)) + begin + AT34C02_SWP_enabled = 1; + Exec_Page_Write_flag = 1; + end +`endif + + if (Exec_Page_Write_flag) + begin // 2-11-2003 + if (VERBOSE) + $display ("Time:", $realtime); + if (VERBOSE) + $display ("STOP DETECTED - TIME OUT FOR PAGE WRITE"); + + disable Data_Transmission; + Time_Out_flag = 1; + Exec_Page_Write_flag = 0; + #TIMEOUT_FOR_WRITE Time_Out_flag = 0; + shift_in = 0; + end + else + begin + if (VERBOSE) + $display ("STOP CONDITION WITHOUT PAGE WRITE", $time); + if (VERBOSE) + $display ("END DATA TRANSMISSION"); + disable Data_Transmission; + end + end + end + + // Watchdog for random START + + always @ (negedge SCL) + begin: Watch_for_Random_START_trigger + if(S_START && First_START_flag) + begin + + if(VERBOSE) + $display(">>> Detected random START protocol interupt", $time); + + disable Data_Transmission.Outer_Block.Inner_Block; + end + end + + task Get_a_Byte; // Reads data on 8 rising edges of SCL + begin + NO_ACK_flag = 0; + shift_in = 1; + repeat (8) + begin + @ (posedge SCL) + if (VERBOSE) + $display ("Within Get_a_Byte"); + if(VERBOSE) + $display("Bit received: %H", S_Byte_Shft_Reg[0]); + end + end + endtask + + task Check_for_Valid_Upper_Bits_of_Device_Address; + begin + Valid_Address_flag = 0; // 7/26/2008 + if (VERBOSE) + $display ("Checking for valid upper bits of device address", $time); +`ifdef AT24C1024 + + Device_Address = S_Byte_Shft_Reg[7: 2]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C1024"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C1024"); + end +`endif +`ifdef AT24C512B + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C512B"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C512B"); + end +`endif + +`ifdef AT24C512 + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C512"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C512"); + end +`endif +`ifdef AT24C256 + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C256"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C256"); + end +`endif +`ifdef AT24C128 + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C128"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C128"); + end +`endif + +`ifdef AT24C64 + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C64"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C64"); + end +`endif +`ifdef AT24C64A + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C64A"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C64A"); + end +`endif + +`ifdef AT24C64B + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C64B"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C64B"); + end +`endif + +`ifdef AT24C64C + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C64C"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C64C"); + end +`endif + +`ifdef AT24C64D + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C64D"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C64D"); + end +`endif + +`ifdef AT24C32 + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C32"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C32"); + end +`endif +`ifdef AT24C32E + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C32E"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C32E"); + end +`endif +`ifdef AT24C32A + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C32A"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C32A"); + end +`endif +`ifdef AT24C16 + Device_Address = S_Byte_Shft_Reg[7: 4]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C16"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C16"); + end +`endif +`ifdef AT24C16A + Device_Address = S_Byte_Shft_Reg[7: 4]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C16A"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C16A"); + + end +`endif +`ifdef AT24C08 + Device_Address = {2'b0, S_Byte_Shft_Reg[7: 3]}; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C08"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + + $display ("Valid device address not detected for AT24C08"); + end +`endif + +`ifdef AT24C08A + Device_Address = {2'b0, S_Byte_Shft_Reg[7: 3]}; + if (VERBOSE) + $display ("SLAVE_ADDRESS =", SLAVE_ADDRESS); + if (VERBOSE) + $display ("Device_Address =", Device_Address); + + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C08A"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C08A"); + end +`endif +`ifdef AT24C04 + Device_Address = S_Byte_Shft_Reg[7: 2]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C04"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C04"); + end +`endif +`ifdef AT24C04A + Device_Address = S_Byte_Shft_Reg[7: 2]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C04A"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C04A"); + end +`endif +`ifdef AT24C02 + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C02"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C02"); + end +`endif +`ifdef AT24C02A + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C02A"); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C02A"); + end +`endif +`ifdef AT24C01A + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT24C01A", $time); + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT24C01", $time); + end +`endif + +`ifdef AT34C02 + Device_Address = S_Byte_Shft_Reg[7: 1]; + if (Device_Address == SLAVE_ADDRESS) + begin + if (VERBOSE) + $display ("Valid device address for AT34C02"); + Valid_Address_flag = 1; + end + else if ((Device_Address == {4'b0110, SLAVE_ADDRESS[3:1]}) + && (AT34C02_SWP_reg == 0)) + begin + if (VERBOSE) + $display ("Device will be programmed for software write protection"); + AT34C02_SWP_reg = 1; + Valid_Address_flag = 1; + end + else if (VERBOSE) + begin + $display ($realtime); + $display ("Valid device address not detected for AT34C02"); + end + +`endif // `ifdef AT34C02 + + end + endtask + + + + task Write_or_Dummy_Write_with_Random_Read; + begin: Wrapper + if (VERBOSE) + $display ("Write or Dummy Write with Random Read", $time); + if (VERBOSE) + $display("******************* Check for Dummy Write Sequence"); + + Get_Lower_Device_Address_Bits_and_ACK_to_Master; + + if (VERBOSE) + $display("Device address bits are loaded"); + if(VERBOSE) + $display("addr_reg holds %H",addr_reg); + Get_Word_Address_Bytes; + if(VERBOSE) + $display("addr_reg holds %H",addr_reg); + shift_in = 1; // Prep for read of data word (gets MSB) + + if (VERBOSE) + $display("Wait for First clock after ACK following address bytes"); + + @ (posedge SCL) // Can't get START condition until after this clock + begin: Byte_or_Page_Write + if (VERBOSE) + $display("Byte_or_Page_Write Sequence initiated", $time); + if (VERBOSE) + $display("Checking for WRITE PROTECTION"); + if (WP == 1) + begin: WP_Detection + if (VERBOSE) + $display ("************************"); + if (VERBOSE) + $display ("Invalid Attempt to Write"); + if (VERBOSE) + $display ("Chip is write-protected"); + if (VERBOSE) + $display ("************************"); + disable Data_Transmission; // 7/26/2008 + end // WP_Detection + + else + begin: Write_Sequence + if (VERBOSE) + $display("Beginning a WRITE sequence", $time); + Get_Remainder_of_Byte; + if(VERBOSE) + $display("S_Byte_Shft_Reg holds %H",S_Byte_Shft_Reg); + if(VERBOSE) + $display ("addr_reg holds %H",addr_reg); + ACK_and_Write_a_Byte; + if (VERBOSE) + $display("Increment the address register", $time); + Execute_Page_Write; + if (VERBOSE) + $display ("*********************************************"); + if (VERBOSE) + $display ("********************** TIMEOUT FOR PAGE WRITE"); + if (VERBOSE) + $display ("*********************************************"); + end // Write_Sequence + end // Byte_or_Page_Write + end // Wrapper + endtask + + + task ACK_and_Write_a_Byte; +`ifdef AT34C02 + + if ((AT34C02_SWP_reg == 1) && (AT34C02_SWP_enabled == 0)) + begin + if(VERBOSE) + $display("ACKing and writing a byte", $time); + @ (negedge SCL); + S_ACK = 1; + shift_in = 0; + S_send_rcvb = SEND; + + @ (posedge SCL) + @ (negedge SCL) + begin + S_ACK = 0; + shift_in = 1; + S_send_rcvb = RECEIVE; + end + else if ((AT34C02_SWP_enabled && (addr_reg > 127))||(AT34C02_SWP_reg == 0)) + begin + @ (negedge SCL) + S_ACK = 1; + shift_in = 0; + S_send_rcvb = SEND; + Write_flag = 1; + @ (posedge SCL) + memory[addr_reg] <= S_Byte_Shft_Reg; + @ (negedge SCL) + S_ACK = 0; + shift_in = 1; + S_send_rcvb = RECEIVE; + Write_flag = 0; + -> incr_word_addr; + + end + +`else + begin // not AT34C02 + @ (negedge SCL) S_ACK = 1; + shift_in = 0; + S_send_rcvb = SEND; + + Write_flag = 1; + @ (posedge SCL) memory[addr_reg] <= S_Byte_Shft_Reg; + @ (negedge SCL) S_ACK = 0; + shift_in = 1; + S_send_rcvb = RECEIVE; + Write_flag = 0; + -> incr_word_addr; + end +`endif + endtask + + task Get_Remainder_of_Byte; + begin + if (VERBOSE) + $display("Getting remainder of byte"); + repeat (7) + begin + @ (posedge SCL) + if(VERBOSE) + $display("Clock tick - Data bit loaded into S_Byte_Shift_Reg"); + if(VERBOSE) + $display("Ready to write - data word has been loaded into shift register", $time); + end + end // repeat + endtask + + + task Get_Lower_Device_Address_Bits_and_ACK_to_Master; + begin // Called on first drop of SCL after byte of data is read + if (VERBOSE) + $display("Getting lower device address bits and ACK to Master", $time); + +`ifdef AT24C1024 + + addr_reg[ADDR_SIZE-1] = S_Byte_Shft_Reg[1]; //Grab P0 +`endif + `ifdef AT24C16 + + addr_reg[ADDR_SIZE-1:ADDR_SIZE-3 ] = S_Byte_Shft_Reg[3:1]; +`endif + `ifdef AT24C16A + + addr_reg[ADDR_SIZE-1:ADDR_SIZE-3 ] = S_Byte_Shft_Reg[3:1]; +`endif + `ifdef AT24C08 + + addr_reg[ADDR_SIZE-1:ADDR_SIZE-2] = S_Byte_Shft_Reg[2:1]; +`endif + `ifdef AT24C08A + + addr_reg[ADDR_SIZE-1:ADDR_SIZE-2] = S_Byte_Shft_Reg[2:1]; +`endif + `ifdef AT24C04 + + addr_reg[ADDR_SIZE-1] = S_Byte_Shft_Reg[1]; +`endif + `ifdef AT24C04A + + addr_reg[ADDR_SIZE-1] = S_Byte_Shft_Reg[1]; +`endif + + S_send_rcvb = SEND; + S_ACK = 1; // Send ACK on first drop of SCL after byte of data + @(negedge SCL) + S_ACK = 0; + shift_in = 1; // Ends ACK on second drop of SCL + S_send_rcvb = RECEIVE; + end + endtask + + task Get_Word_Address_Bytes; + begin + if(VERBOSE) + $display("Get_Word_Address_Bytes"); + case (MEM_SIZE) + MEM_SIZE_1MEG_BIT, + MEM_SIZE_500K_BIT, + MEM_SIZE_256K_BIT, + MEM_SIZE_128K_BIT, + MEM_SIZE_64K_BIT, + MEM_SIZE_32K_BIT: + begin + Get_MSB_Byte_and_ACK; + shift_in = 1; + Get_LSB_Byte_and_ACK; + end + default: + begin + Get_LSB_Byte_and_ACK; + end + endcase + end + endtask + + task Begin_a_Read_Sequence; + begin + if (VERBOSE) + $display("Beginning a READ sequence", $time); + @ (negedge SCL) + shift_in = 0; + NO_ACK_flag = 0; + if (Device_Address_Active) + Check_for_Valid_Upper_Bits_of_Device_Address; + if (Valid_Address_flag) + begin: Address_Match + if (VERBOSE) + $display ("Upper_Bits_of_Device_Address_Match"); + + case (S_Byte_Shft_Reg[0]) + 0: + begin + Write_or_Dummy_Write_with_Random_Read;// Should not occur + if (VERBOSE) + $display ("********* ERROR *****"); + end + 1: + Current_Address_and_Sequential_Read; // Read after dummy write + endcase + end // Address_Match + end + endtask + + + /////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////// + task Execute_Page_Write; // Added 1-27-2003 + begin + if (VERBOSE) + $display("time:", $realtime); + if (VERBOSE) + $display("*********************************************"); + if (VERBOSE) + $display("*********************************************"); + if (VERBOSE) + $display("********* Execute_Page_Write is Writing Bytes"); + if (VERBOSE) + $display("*********************************************"); + if (VERBOSE) + $display("*********************************************"); + if (VERBOSE) + $display("************************************", $time); +`ifdef AT34C02 begin + + if ((AT34C02_SWP_enabled && (addr_reg > 127))||(AT34C02_SWP_reg == 0)) + begin + shift_in = 1; + Exec_Page_Write_flag = 1; + forever + begin: Writing_Bytes + repeat (BYTE_SIZE) @ (posedge SCL); + ACK_and_Write_a_Byte; + addr_reg[WORD_ADDR_SIZE-1: 0] <= addr_reg[WORD_ADDR_SIZE-1:0] +1; + end // Writing_Bytes + end + +`else // Not AT34C02 + shift_in = 1; + Exec_Page_Write_flag = 1; + forever + begin: Writing_Bytes + repeat (BYTE_SIZE) @ (posedge SCL); + ACK_and_Write_a_Byte; + addr_reg[WORD_ADDR_SIZE-1: 0] <= addr_reg[WORD_ADDR_SIZE-1:0] +1; + end // Writing_Bytes +`endif + + end + endtask + + task Current_Address_and_Sequential_Read; + begin + + if (VERBOSE) + $display("Beginning Current_Address_and_Sequential_Read", $time); + Load_Slave_Shift_Register_and_Send_ACK; + @(posedge SCL) + ld_S_Byte_Shft_Reg = 0; + shift_out = 1; + @(negedge SCL) + forever + begin: Current_or_Sequential_Read + Exec_Seq_Read_flag = 1; + repeat (6) @ (negedge SCL); + @ (posedge SCL) shift_out = 0; + @ (negedge SCL) // Have sent ACK and 8 bits to master + S_send_rcvb = RECEIVE; + ->incr_addr_reg; + @ (posedge SCL) + if (SDA_in > 0) + begin :Got_NO_ACK_from_Master + NO_ACK_So_Wait_for_STOP_Condition; + if (VERBOSE) + $display ("Got no ACK from Master - Disable Data Transmission", $time); + Exec_Seq_Read_flag = 0; + disable Data_Transmission; + end //Got_No_ACK_from_Master + else + begin: Got_ACK_from_Master // bit was 0 + + ld_S_Byte_Shft_Reg = 1; + + Got_ACK_so_prepare_to_send; + end // Got_ACK_from_Master + end // Current _or_Sequential_Read + end + + endtask + + task Load_Slave_Shift_Register_and_Send_ACK; // Modified for protocol interupt + begin + S_send_rcvb = SEND; + S_ACK = 1; // Send ACK to master + @(posedge SCL) + ld_S_Byte_Shft_Reg = 1; + @ (negedge SCL) + if(S_START) + begin + if(VERBOSE) + $display("Protocol interupted by START", $time); + disable Data_Transmission.Outer_Block.Inner_Block; + end + S_ACK = 0; + end + endtask + + task Got_ACK_so_prepare_to_send; //Modified for protocol interupt + begin + if (VERBOSE) + $display ("got ACK from master"); + @ (negedge SCL) + if(S_START) + begin + if(VERBOSE) + $display("Protocol interupted by START", $time); + disable Data_Transmission.Outer_Block.Inner_Block; + end + S_send_rcvb = SEND; + @ (posedge SCL); + ld_S_Byte_Shft_Reg = 0; + shift_out = 1; + @ (negedge SCL) + if(S_START) + begin + if(VERBOSE) + $display("Protocol interupted by START", $time); + disable Data_Transmission.Outer_Block.Inner_Block; + end + + end + endtask + + task NO_ACK_So_Wait_for_STOP_Condition; // Modified for protocol interupt + begin + NO_ACK_flag = 1; + if (VERBOSE) + $display ("Got_NO_ACK_from_Master ", $time); + ld_S_Byte_Shft_Reg = 0; + @ (STOP_condition) + NO_ACK_flag = 0; + S_START = 0; // 04/24/2006 + S_STOP = 1; // 04/24/2006 + end + endtask + + task Get_MSB_Byte_and_ACK; // Modified for protocol interupt + begin + if (VERBOSE) + $display("Get_MSB_Byte_and_ACK", $time); + Get_a_Byte; + @ (negedge SCL) + shift_in = 0; + ld_addr_reg_MSB_byte = 1; // Set to load on next up edge of SCL + S_send_rcvb = SEND; + S_ACK = 1; // Send ACK to master + @ (negedge SCL) + if (VERBOSE) + $display ("Loading MSB address byte", $time); + ld_addr_reg_MSB_byte = 0; + + S_send_rcvb = RECEIVE; + S_ACK = 0; + end + endtask + + task Get_LSB_Byte_and_ACK; //Modified for protocol interupt + begin + if (VERBOSE) + $display("Get_LSB_Byte_and_ACK", $time); + //shift_in = 1; + Get_a_Byte; + @ (negedge SCL); + shift_in = 0; + ld_addr_reg_LSB_byte = 1; + S_send_rcvb = SEND; + S_ACK = 1; // Send ACK to master + @ (negedge SCL) + if (VERBOSE) + $display("Loading LSB address byte", $time); + ld_addr_reg_LSB_byte = 0; + S_send_rcvb = RECEIVE; + S_ACK = 0; + end + endtask + + task Power_up_initialization; + begin + RUN_flag = 0; + addr_reg = 0; + S_Byte_Shft_Reg = 0; + Protocol_Recovery_reg = 0; + + ld_S_Byte_Shft_Reg = 0; + ld_addr_reg_MSB_byte = 0; + ld_addr_reg_LSB_byte = 0; + shift_in = 0; + shift_out = 0; + S_ACK = 0; + NO_ACK_flag = 0; + Time_Out_flag = 0; + Exec_Page_Write_flag = 0; + + Exec_Seq_Read_flag = 0; + Exec_Random_Read_flag = 0; + Write_flag = 0; + First_START_flag = 0; + + S_START = 0; + S_STOP = 0; + Device_Address_Active = 0; + + // #POWER_UP_DELAY if (VERBOSE) $display ("Power_up_Initialization"); CJT 06_22_2020 + RUN_flag = 1; + + end + endtask + + task Recover_from_Protocol_Interupt; + begin + Valid_Address_flag = 0; + ld_S_Byte_Shft_Reg = 0; + ld_addr_reg_MSB_byte = 0; + ld_addr_reg_LSB_byte = 0; + S_Byte_Shft_Reg = 0; + shift_in = 0; + shift_out = 0; + S_ACK = 0; + NO_ACK_flag = 0; + Exec_Page_Write_flag = 0; + Exec_Seq_Read_flag = 0; + Exec_Random_Read_flag = 0; + Write_flag = 0; + S_START = 0; + S_STOP = 0; + S_send_rcvb = RECEIVE; + end + endtask + + + + + //*************************************************************************** + //*************************************************************************** + // Timing Parameters and Checks AT24C1024 + //*************************************************************************** + //*************************************************************************** + // Set up conditions for when to measure stop timing + wire en_st_stp_timing = RUN_flag && SCL && RECEIVE; + + /* __________________________________________________ * + * | Symbol | Voltage | Min | Max | Units | * + * |---------|------------|--------|-------|--------| * + * | fSCL | 4.5 - 5.5 | 0 | 1000 | kHz | * + * | | 2.7 - 5.5 | 0 | 400 | | * + * |---------|------------|--------|-------|--------| * + * | tLOW | 4.5 - 5.5 | 0.6 | 0 | us | * + * | | 2.7 - 5.5 | 1.3 | 0 | | * + * |---------|------------|--------|-------|--------| * + * | tHIGH | 4.5 - 5.5 | 0.4 | 0 | us | * + * | | 2.7 - 5.5 | 1.0 | 0 | | * + * |---------|------------|--------|-------|--------| * + * | tHDSTA | 4.5 - 5.5 | 0.25 | | us | * + * | | 2.7 - 5.5 | 0.6 | | | * + * |---------|------------|--------|-------|--------| * + * | tSUSTA | 4.5 - 5.5 | 0.25 | | us | * + * | | 2.7 - 5.5 | 0.6 | | | * + * |---------|------------|--------|-------|--------| * + * | tHDDAT | | | 0 | ns | * + * |---------|------------|--------|-------|--------| * + * | tSUDAT | | | 100 | ns | * + * |---------|------------|--------|-------|--------| * + * | tSUSTO | 4.5 - 5.5 | 0.25 | | us | * + * | | 2.7 - 5.5 | 0.6 | | | * + * |---------|------------|--------|-------|--------| * + * | tDH | | 50 | | ns | * + * ------------------------------------------------ * + * * + ****************************************************************************/ + +`ifdef WANT_AT24C1024__4_5__5_5V_TIMING + + specify + specparam tLOW = 400; + specparam tHIGH = 400; + specparam tHDSTA = 250; + specparam tSUSTA = 250; + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 250; + specparam tDH = 50; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + +`ifdef WANT_AT24C1024__2_7__5_5V_TIMING + + specify + specparam tLOW = 1300; + specparam tHIGH = 600; + specparam tHDSTA = 600; + specparam tSUSTA = 600; + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 600; + specparam tDH = 50; + + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + + //*************************************************************************** + + //*************************************************************************** + // Timing Parameters and Checks AT24C01A/02/04/08/16 + //*************************************************************************** + //*************************************************************************** + /* ----------------------------------------------------- + * | Voltage | Voltage | | + * | 2.7-,2.5-, 1.8 V | 5 V | | + * |---------|---------|-------|--------|-------|-------| + * | Symbol | Min | Max | Min | Max | Units | + * |---------|---------|-------|--------|-------|-------| + * | fSCL | 0 | 100 | | 400 | kHz | + * |---------|---------|-------|-------_|-------|-------| + * | tLOW | 4.7 | 0 | 1.2 | | us | + * |---------|---------|-------|--------|-------|-------| + * | tHIGH | 4 | 0 | 0.6 | | us | + * |---------|---------|-------|--------|-------|-------| + * | tHDSTA | 4 | | 0.6 | | us | + * |---------|---------|-------|--------|-------|-------| + * | tSUSTA | 4.7 | | 0.6 | | us | + * |---------|---------|-------|--------|-------|-------| + * | tHDDAT | 0 | | 0 | | us | + * |---------|---------|-------|--------|-------|-------| + * | tSUDAT | 200 | | 100 | | ns | + * |---------|---------|-------|--------|-------|-------| + * | tSUSTO | 4.7 | | 0.6 | | us | + * |---------|---------|-------|--------|-------|-------| + * | tDH | 100 | | 50 | | ns | + * ---------------------------------------------------- * + + ****************************************************************************/ + + + +`ifdef WANT_AT24C_01A_02_04_08_16__5_V_TIMING + + specify + specparam tLOW = 1200; + specparam tHIGH = 600; + specparam tHDSTA = 600; + specparam tSUSTA = 600; + + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 600; + specparam tDH = 50; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + +`ifdef WANT_AT24C_01A_02_04_08_16__2_7__2_5__1_8_V_TIMING + + specify + specparam tLOW = 1200; + specparam tHIGH = 600; + specparam tHDSTA = 600; + specparam tSUSTA = 600; + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 600; + specparam tDH = 50; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + + + + //*************************************************************************** + //*************************************************************************** + // Timing Parameters and Checks + // AT24C02A/04A/08A/16A + //*************************************************************************** + //*************************************************************************** + // ----------------------------------------------------------------- + // | |AT24C02A/04A |AT24C02A/04A | AT24C16A |AT24C02A/04A | + // | |AT24C08A/16A |AT24C08A | |AT24C08A/16A | + // |----------------------------------------------------------------- + // | | Voltage | Voltage | | | + // | | 1.8V | 2.5V, 2.7V | 2.5V | 5.0V | + // |---------|------|------|------|------|------|------|--------------------| + // | Symbol | Min | Max | Min | Max | Min | Max | Min | Max |Units | + // |---------|------|------|------|------|------|------|------|------|------| + // | fSCL | | 100 | | 100 | | 400 | | 400 | kHz | + // |---------|------|------|------|------|------|------|------|------|------| + // | tLOW | 4.7 | | 4.7 | | 1.3 | | 1.2 | | us | + // |---------|------|------|------|------|------|------|------|------|------| + // | tHIGH | 4.0 | | 4.0 | | 0.6 | | 0.6 | | us | + // |---------|------|------|------|------|------|------|------|------|------| + // | tHDSTA | 4.0 | | 4.0 | | 0.6 | | 0.6 | | us | + // |---------|------|------|------|------|------|------|------|------|------| + // | tSUSTA | 4.7 | | 4.7 | | 0.6 | | 0.6 | | us | + // |---------|------|------|------|------|------|------|------|------|------| + // | tHDDAT | 0 | | 0 | | 0 | | 0 | | us | + // |---------|------|------|------|------|------|------|------|------|------| + // | tSUDAT | 200 | | 200 | | 100 | | 100 | | ns | + // |---------|------|------|------|------|------|------|------|------|------| + // | tSUSTO | 4.7 | | 4.7 | | 0.6 | | 0.6 | | us | + // |---------|------|------|------|------|------|------|------|------|------| + // | tDH | 100 | | 100 | | 100 | | 50 | | ns | + // ------------------------------------------------------------------------| + + +`ifdef WANT_AT24C_02A_04A_08A_16A__1_8_V_TIMING + + specify + specparam tLOW = 4700; + specparam tHIGH = 4000; + specparam tHDSTA = 4000; + specparam tSUSTA = 4700; + + specparam tHDDAT = 0; + specparam tSUDAT = 200; + specparam tSUSTO = 4700; + specparam tDH = 100; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + +`ifdef WANT_AT24C_02A_04A_08A__2_5V__2_7V_TIMING + + specify + specparam tLOW = 4700; + specparam tHIGH = 4000; + specparam tHDSTA = 4000; + specparam tSUSTA = 4700; + specparam tHDDAT = 0; + specparam tSUDAT = 200; + specparam tSUSTO = 4700; + specparam tDH = 100; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + +`ifdef WANT_AT24C_16A__2_5V_TIMING + + specify + specparam tLOW = 1300; + specparam tHIGH = 600; + specparam tHDSTA = 600; + specparam tSUSTA = 600; + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 600; + specparam tDH = 100; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + + +`ifdef WANT_AT24C_02A_04A_08A_16A__5V_TIMING + + specify + specparam tLOW = 1200; + specparam tHIGH = 600; + specparam tHDSTA = 600; + specparam tSUSTA = 600; + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 600; + specparam tDH = 50; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + + + + //*************************************************************************** + //*************************************************************************** + // Timing Parameters and Checks + // AT24C512B + //*************************************************************************** + //*************************************************************************** + // |---------------------------------------------------- + // | | Voltage | Voltage | + // | | 1.8V | 2.5V / 5.0 V | + // |---------|------|------|------|------|------| + // | Symbol | Min | Max | Min | Max |Units | + // |---------|------|------|------|------|------| + // | fSCL | | 400 | | 1000 | kHz | + // |---------|------|------|------|------|------| + // | tLOW | 1.3 | | 0.4 | | us | + // |---------|------|------|------|------|------| + // | tHIGH | 0.6 | | 0.4 | | us | + // |---------|------|------|------|------|------| + // | tHDSTA | 0.6 | | 0.25 | | us | + // |---------|------|------|------|------|------| + // | tSUSTA | 0.6 | | 0.25 | | us | + // |---------|------|------|------|------|------| + // | tHDDAT | 0 | | 0 | | us | + // |---------|------|------|------|------|------| + // | tSUDAT | 100 | | 100 | | ns | + // |---------|------|------|------|------|------| + // | tSUSTO | 0.6 | | 0.25 | | us | + // |---------|------|------|------|------|------| + // | tDH | 50 | | 50 | | ns | + // --------------------------------------------| + +`ifdef WANT_AT24C512B__1_8V_TIMING + + specify + specparam tLOW = 400; + specparam tHIGH = 400; + specparam tHDSTA = 250; + specparam tSUSTA = 250; + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 600; + specparam tDH = 50; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + +`ifdef WANT_AT24C512B__2_5V_TIMING + + specify + specparam tLOW = 400; + specparam tHIGH = 400; + specparam tHDSTA = 250; + specparam tSUSTA = 250; + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 250; + specparam tDH = 50; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + + + + //*************************************************************************** + //*************************************************************************** + // Timing Parameters and Checks + // AT24C512 + //*************************************************************************** + //*************************************************************************** + // |---------------------------------------------------- + // | | Voltage | Voltage | Voltage | + // | | 1.8V | 2.7V | 5.0 V | + // |---------|------|------|------|------|------|------|------| + // | Symbol | Min | Max | Min | Max | Min | Max |Units | + // |---------|------|------|------|------|------|------|------| + // | fSCL | | 100 | | 100 | | 400 | kHz | + // |---------|------|------|------|------|------|------|------| + // | tLOW | 4.7 | | 1.3 | | 0.4 | | us | + // |---------|------|------|------|------|------|------|------| + // | tHIGH | 4.0 | | 1.0 | | 0.4 | | us | + // |---------|------|------|------|------|------|------|------| + // | tHDSTA | 4.0 | | 0.6 | | 0.25 | | us | + // |---------|------|------|------|------|------|------|------| + // | tSUSTA | 4.7 | | 0.6 | | 0.25 | | us | + // |---------|------|------|------|------|------|------|------| + // | tHDDAT | 0 | | 0 | | 0 | | us | + // |---------|------|------|------|------|------|------|------| + // | tSUDAT | 200 | | 100 | | 100 | | ns | + // |---------|------|------|------|------|------|------|------| + // | tSUSTO | 4.7 | | 0.6 | | 0.25 | | us | + // |---------|------|------|------|------|------|------|------| + // | tDH | 100 | | 50 | | 50 | | ns | + // ----------------------------------------------------------| + +`ifdef WANT_AT24C512__1_8_V_TIMING + + specify + specparam tLOW = 4700; + specparam tHIGH = 4000; + specparam tHDSTA = 4000; + specparam tSUSTA = 4700; + specparam tHDDAT = 0; + specparam tSUDAT = 200; + specparam tSUSTO = 4700; + specparam tDH = 100; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + +`ifdef WANT_AT24C512__2_7V_TIMING + + specify + specparam tLOW = 400; + specparam tHIGH = 400; + specparam tHDSTA = 250; + specparam tSUSTA = 250; + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 600; + specparam tDH = 50; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + +`ifdef WANT_AT24C512__5V_TIMING + + specify + specparam tLOW = 1300; + specparam tHIGH = 600; + specparam tHDSTA = 600; + specparam tSUSTA = 600; + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 250; + specparam tDH = 50; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + + ////////////////// + + //*************************************************************************** + //*************************************************************************** + // Timing Parameters and Checks + // AT24C128_256 + //*************************************************************************** + //*************************************************************************** + // |--------------------------------------------------- + // | | Voltage | Voltage | Voltage | + // | | 1.8V | 2.5V | 5.0 V | + // |---------|------|------|------|------|------|------|------| + // | Symbol | Min | Max | Min | Max | Min | Max |Units | + // |---------|------|------|------|------|------|------|------| + // | fSCL | | 100 | | 400 | | 1000 | kHz | + // |---------|------|------|------|------|------|------|------| + // | tLOW | 4.7 | | 1.3 | | 0.4 | | us | + // |---------|------|------|------|------|------|------|------| + // | tHIGH | 4.0 | | 0.6 | | 0.4 | | us | + // |---------|------|------|------|------|------|------|------| + // | tHDSTA | 4.0 | | 0.6 | | 0.25 | | us | + // |---------|------|------|------|------|------|------|------| + // | tSUSTA | 4.7 | | 0.6 | | 0.25 | | us | + // |---------|------|------|------|------|------|------|------| + // | tHDDAT | 0 | | 0 | | 0 | | us | + // |---------|------|------|------|------|------|------|------| + // | tSUDAT | 200 | | 100 | | 100 | | ns | + // |---------|------|------|------|------|------|------|------| + // | tSUSTO | 4.7 | | 0.6 | | 0.25 | | us | + // |---------|------|------|------|------|------|------|------| + // | tDH | 100 | | 50 | | 50 | | ns | + // ----------------------------------------------------------| + + +`ifdef WANT_AT24C128_256__1_8_V_TIMING + + specify + specparam tLOW = 4700; + specparam tHIGH = 4000; + specparam tHDSTA = 4000; + specparam tSUSTA = 4700; + specparam tHDDAT = 0; + specparam tSUDAT = 200; + specparam tSUSTO = 4700; + specparam tDH = 100; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + +`ifdef WANT_AT24C128_256__2_5V_TIMING + + specify + specparam tLOW = 1300; + specparam tHIGH = 600; + specparam tHDSTA = 600; + specparam tSUSTA = 600; + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 600; + specparam tDH = 50; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + +`ifdef WANT_AT24C128_256__5V_TIMING + + specify + specparam tLOW = 400; + + specparam tHIGH = 400; + specparam tHDSTA = 250; + specparam tSUSTA = 250; + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 250; + specparam tDH = 50; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + + + //*************************************************************************** + //*************************************************************************** + // Timing Parameters and Checks AT34C02 + //*************************************************************************** + //*************************************************************************** + + /* ----------------------------------------------------- + * | Voltage | Voltage | | + * | 1.8 V | 2.7, 5 V | | + * |---------|---------|-------|--------|-------|-------| + * | Symbol | Min | Max | Min | Max | Units | + * |---------|---------|-------|--------|-------|-------| + * | fSCL | 0 | 100 | | 400 | kHz | + * |---------|---------|-------|-------_|-------|-------| + * | tLOW | 4.7 | 0 | 1.2 | | us | + * |---------|---------|-------|--------|-------|-------| + * | tHIGH | 4 | 0 | 0.6 | | us | + * |---------|---------|-------|--------|-------|-------| + * | tHDSTA | 4 | | 0.6 | | us | + * |---------|---------|-------|--------|-------|-------| + * | tSUSTA | 4.7 | | 0.6 | | us | + * |---------|---------|-------|--------|-------|-------| + * | tHDDAT | 0 | | 0 | | us | + * |---------|---------|-------|--------|-------|-------| + * | tSUDAT | 200 | | 100 | | ns | + * |---------|---------|-------|--------|-------|-------| + * | tSUSTO | 4.7 | | 0.6 | | us | + * |---------|---------|-------|--------|-------|-------| + * | tDH | 100 | | 50 | | ns | + * ---------------------------------------------------- + * + ****************************************************************************/ + +`ifdef WANT_AT34C02__2_7__5_0_V_TIMING + + specify + specparam tLOW = 1200; + specparam tHIGH = 600; + specparam tHDSTA = 600; + specparam tSUSTA = 600; + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 600; + specparam tDH = 50; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + + +`ifdef WANT_AT34C02__1_8_V_TIMING + + specify + specparam tLOW = 4700; + specparam tHIGH = 4000; + specparam tHDSTA = 4000; + specparam tSUSTA = 4700; + + specparam tHDDAT = 0; + specparam tSUDAT = 200; + specparam tSUSTO = 4700; + specparam tDH = 100; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif // `ifdef WANT_AT34C02__1_8_V_TIMING + + //*************************************************************************** + //*************************************************************************** + // Timing Parameters and Checks AT34C32/64 + //*************************************************************************** + //*************************************************************************** + + /* ----------------------------------------------------- + * | Voltage | Voltage | | + * | 1.7 V | 2.5, 3.6 V | | + * |---------|---------|-------|--------|-------|-------| + * | Symbol | Min | Max | Min | Max | Units | + * |---------|---------|-------|--------|-------|-------| + * | fSCL | 0 | 400 | |1000 | kHz | + * |---------|---------|-------|-------_|-------|-------| + * | tLOW | 1300 | 0 | 500 | | ns | + * |---------|---------|-------|--------|-------|-------| + * | tHIGH | 600 | 0 | 400 | | ns | + * |---------|---------|-------|--------|-------|-------| + * | tHDSTA | 600 | | 250 | | ns | + * |---------|---------|-------|--------|-------|-------| + * | tSUSTA | 600 | | 250 | | ns | + * |---------|---------|-------|--------|-------|-------| + * | tHDDAT | 0 | | 0 | | ns | + * |---------|---------|-------|--------|-------|-------| + * | tSUDAT | 100 | | 100 | | ns | + * |---------|---------|-------|--------|-------|-------| + * | tSUSTO | 600 | | 250 | | ns | + * |---------|---------|-------|--------|-------|-------| + * | tDH | 100 | | 50 | | ns | + * ---------------------------------------------------- * + ****************************************************************************/ + +`ifdef WANT_AT34C32__1_7_V_TIMING + + specify + specparam tLOW = 1300; + specparam tHIGH = 600; + specparam tHDSTA = 600; + specparam tSUSTA = 600; + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 600; + specparam tDH = 100; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif + + +`ifdef WANT_AT34C32__2_5_V_TIMING + + specify + specparam tLOW = 500; + specparam tHIGH = 400; + specparam tHDSTA = 250; + specparam tSUSTA = 2500; + + specparam tHDDAT = 0; + specparam tSUDAT = 100; + specparam tSUSTO = 250; + specparam tDH = 50; + + $width(posedge SCL, tHIGH); // SCL pulsewidths + $width(negedge SCL, tLOW); + + $hold(negedge SDA, negedge SCL, tHDSTA); // START hold time cjt 02_27_2020 + $setup(posedge SCL, negedge SDA &&& en_st_stp_timing, tSUSTA); // START setup time cjt 02_27_2020 + + + $hold(negedge SCL, SDA, tHDDAT); // Data_in hold time + $setup(SDA, posedge SCL, tSUDAT); // Data_in setup time + + $setup(posedge SCL, posedge SDA &&& en_st_stp_timing, tSUSTO); // STOP setup time cjt 02_27_2020 + endspecify +`endif // `ifdef WANT_AT34C32__2_5_V_TIMING + + + endmodule \ No newline at end of file diff --git a/rtl/model/mic.sv b/rtl/model/mic.sv new file mode 100644 index 00000000..339c7f8d --- /dev/null +++ b/rtl/model/mic.sv @@ -0,0 +1,38 @@ +// Copyright (c) 2023-2024 Miao Yuchi +// i2s is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +`include "config.svh" + +module mic #( + parameter int DATA_WIDTH = 24 +) ( + input logic sck_i, + input logic ws_i, + output logic sd_o +); + + logic [DATA_WIDTH-1:0] r_right_chnl_dat = '0; + logic [DATA_WIDTH-1:0] r_left_chnl_dat = '0; + logic r_ws = '0; + + always @(posedge sck_i) r_ws <= #`REGISTER_DELAY ws_i; + always @(negedge r_ws) r_left_chnl_dat <= #`REGISTER_DELAY $random; + always @(posedge r_ws) r_right_chnl_dat <= #`REGISTER_DELAY $random; + + always @(negedge sck_i) + if (~r_ws) begin + sd_o <= #`REGISTER_DELAY r_left_chnl_dat[DATA_WIDTH-1]; + r_left_chnl_dat <= #`REGISTER_DELAY r_left_chnl_dat << 1; + end else begin + sd_o <= #`REGISTER_DELAY r_right_chnl_dat[DATA_WIDTH-1]; + r_right_chnl_dat <= #`REGISTER_DELAY r_right_chnl_dat << 1; + end + +endmodule diff --git a/rtl/model/w25q128jvxim.sv b/rtl/model/w25q128jvxim.sv new file mode 100644 index 00000000..841c455a --- /dev/null +++ b/rtl/model/w25q128jvxim.sv @@ -0,0 +1,2959 @@ +/****************************************************************************** + Winbond Electronics Corporation + Verilog Model for W25Q128JVxIM Serial Flash Memory + + V1.0-beta + + Copyright (c) 2001-2016 Winbond Electronics Corporation + All Rights Reserved. + + Author/Support: Mike Chen + Email: mchen4@winbond.com + + Versions: + 01/30/2016 V1.0-beta Initial Version + +******************************************************************************/ + +`timescale 1ns / 1ns + +module W25Q128JVxIM (CSn, CLK, DIO, DO, WPn, HOLDn); +input CSn, CLK; +inout DIO; +inout WPn; +inout HOLDn; +inout DO; + +parameter NUM_PAGES = 65536; +parameter NUM_SEC_PAGES = 3; +parameter PAGESIZE = 256; +parameter SECTORSIZE = 4096; +parameter HALFBLOCKSIZE = 32768; +parameter BLOCKSIZE = 65536; +parameter NUM_BLOCKS = NUM_PAGES * PAGESIZE / BLOCKSIZE; +parameter NUM_LOCKBITS = NUM_BLOCKS - 2 + 32; +parameter MANUFACTURER = 8'hEF; +parameter DEVICE_ID = 8'h17; +parameter JEDEC_ID_HI = 8'h70; +parameter JEDEC_ID_LO = 8'h18; +parameter UNIQUE_ID = 64'h0102030405060708; +parameter ADDRESS_MASK = (NUM_PAGES * PAGESIZE) - 1; // Note that NUM_PAGES must be a power of 2 for this simulation to work properly. + +`define MEM_FILENAME "MEM.TXT" // Memory contents file(s) +`define SECSI_FILENAME "SECSI.TXT" +`define SFDP_FILENAME "SFDP.TXT" +`define SREG_FILENAME "SREG.TXT" + + +// The following registers define the main memory spaces of the device. +// +// + +reg [7:0] memory [0:(NUM_PAGES * PAGESIZE) - 1]; // Main Memory Array +reg [7:0] page_latch [0:PAGESIZE-1]; // Page Latch for Program Sector commands. +reg [7:0] secsi[0:(NUM_SEC_PAGES * (SECTORSIZE/PAGESIZE) * PAGESIZE) - 1]; // Security Sector Array +reg [7:0] sfdp[0:PAGESIZE]; // Serial Flash Discoverable Parameter +reg lock_array[0:NUM_LOCKBITS-1]; // 510 blocks + 32 sectors + +reg [23:0] status_reg; // Status Register +reg [23:0] status_reg_shadow; // Status Register Shadow Register +reg status_reg_otp [0:23]; // Status Register OTP Bits +reg [23:0] byte_address; // Page address used for reading pages. +reg [23:0] prog_byte_address; // Page address used for writing pages. +reg [7:0] mode_reg; // Mode Register +reg [7:0] wrap_reg; // burst-wrap register +reg [7:0] read_param_reg; // Read parameters register +reg [7:0] read_param_reg_shadow; // Shadow register + +reg flag_prog_page; // Flag used to start the program process for sram 0 +reg flag_prog_secsi_page; // Flag used to program a SECSI page +reg flag_erase_sector; // Flag used to erase a sector (4KB) +reg flag_erase_secsi_sector; +reg flag_erase_half_block; // Flag to erase half a block (32KB) +reg flag_erase_block; // Flag used to erase a block (64KB) +reg flag_erase_bulk; // Flag used to erase the chip +reg flag_power_up_exec; +reg flag_power_down; +reg flag_power_up_sig_read; +reg flag_write_status_reg; +reg flag_suspend; // Flag used to start an erase suspend process. +reg flag_resume; // Flag used to resume from and erase suspend +reg flag_suspend_enabled; // Flag used to stop the erase process. +reg flag_slow_read_reg; +wire flag_slow_read = flag_slow_read_reg; +reg flag_volatile_sr_write; // flag for volatile status register write. +reg flag_read_op_reg; +wire #1 flag_read_op = flag_read_op_reg; // Delay avoids race condition +reg flag_qpi_mode; // flag used for QPI Mode. +reg flag_enable_reset; // flag used to enable the software reset command. +reg flag_reset; // flag to execute the chip reset routine +reg flag_reset_condition; // Flag to stop delay loops +reg flag_set_read_param; // Flag to set read parameters + +reg timing_error; // Register for identifying timing errors when they occur + +reg [7:0] in_byte; // These two variables are used for debug purposes. +reg [7:0] out_byte; + +// Logic output for WPn pin. +// +reg WPn_Reg, WPn_Output_Enable_reg; +wire WPn_Output_Enable = WPn_Output_Enable_reg; + +// Logic output for HOLDn pin. +// +reg HOLDn_Reg, HOLDn_Output_Enable; + +// Logic output for DO pin. +// +reg DO_Reg, DO_Output_Enable, temp_DO_Output_Enable; + +// Logic output for DIO pin. +// +reg DIO_Reg, DIO_Output_Enable_reg, temp_DIO_Output_Enable_reg; +wire DIO_Output_Enable = DIO_Output_Enable_reg; + +// Flag to signal that HOLDn goes active while CSn is low +reg HOLDn_Active; + +// The following are working variables for the simulation program +// + +reg [7:0] cmd_byte; +reg [7:0] null_reg; +reg [7:0] temp; + +integer x; +integer fileno; +reg [15:0] file_sector; +reg [15:0] file_length; + + +// The following macro's define the supported commands in this +// Simulation +// + +`define CMD_WRITE_DISABLE 8'h04 +`define CMD_WRITE_ENABLE 8'h06 +`define CMD_READ_STATUS 8'h05 +`define CMD_WRITE_STATUS 8'h01 +`define CMD_READ_STATUS2 8'h35 +`define CMD_WRITE_STATUS2 8'h31 +`define CMD_READ_STATUS3 8'h15 +`define CMD_WRITE_STATUS3 8'h11 +`define CMD_READ_DATA 8'h03 +`define CMD_READ_DATA_FAST 8'h0B +`define CMD_READ_DATA_FAST_WRAP 8'h0C +`define CMD_READ_DATA_FAST_DTR 8'h0D +`define CMD_READ_DATA_FAST_DTR_WRAP 8'h0E +`define CMD_READ_DATA_FAST_DUAL 8'h3B +`define CMD_READ_DATA_FAST_DUAL_IO 8'hBB +`define CMD_READ_DATA_FAST_DUAL_IO_DTR 8'hBD +`define CMD_READ_DATA_FAST_QUAD 8'h6B +`define CMD_READ_DATA_FAST_QUAD_IO 8'hEB +`define CMD_READ_DATA_FAST_QUAD_IO_DTR 8'hED +`define CMD_PAGE_PROGRAM 8'h02 +`define CMD_PAGE_PROGRAM_QUAD 8'h32 +`define CMD_BLOCK_ERASE 8'hD8 +`define CMD_HALF_BLOCK_ERASE 8'h52 +`define CMD_SECTOR_ERASE 8'h20 +`define CMD_BULK_ERASE 8'hC7 +`define CMD_BULK_ERASE2 8'h60 +`define CMD_DEEP_POWERDOWN 8'hB9 +`define CMD_READ_SIGNATURE 8'hAB +`define CMD_READ_ID 8'h90 +`define CMD_READ_ID_DUAL 8'h92 +`define CMD_READ_ID_QUAD 8'h94 +`define CMD_READ_JEDEC_ID 8'h9F +`define CMD_READ_UNIQUE_ID 8'h4B +`define CMD_SUSPEND 8'h75 +`define CMD_RESUME 8'h7A +`define CMD_SET_BURST_WRAP 8'h77 +`define CMD_MODE_RESET 8'hFF +`define CMD_DISABLE_QPI 8'hFF +`define CMD_ENABLE_QPI 8'h38 +`define CMD_ENABLE_RESET 8'h66 +`define CMD_CHIP_RESET 8'h99 +`define CMD_SET_READ_PARAM 8'hC0 +`define CMD_SREG_PROGRAM 8'h42 +`define CMD_SREG_ERASE 8'h44 +`define CMD_SREG_READ 8'h48 +`define CMD_WRITE_ENABLE_VSR 8'h50 +`define CMD_READ_SFDP 8'h5A +`define CMD_INDIVIDUAL_LOCK 8'h36 +`define CMD_INDIVIDUAL_UNLOCK 8'h39 +`define CMD_READ_BLOCK_LOCK 8'h3D +`define CMD_GLOBAL_BLOCK_LOCK 8'h7E +`define CMD_GLOBAL_BLOCK_UNLOCK 8'h98 + + +// Status register definitions +// + +`define STATUS_HLD_RST 24'h800000 +`define STATUS_DRV1 24'h400000 +`define STATUS_DRV0 24'h200000 +`define STATUS_WPS 24'h040000 +`define STATUS_SUS 24'h008000 +`define STATUS_CMP 24'h004000 +`define STATUS_LB3 24'h002000 +`define STATUS_LB2 24'h001000 +`define STATUS_LB1 24'h000800 +`define STATUS_QE 24'h000200 +`define STATUS_SRL 24'h000100 +`define STATUS_SRP 24'h000080 +`define STATUS_SEC 24'h000040 +`define STATUS_TB 24'h000020 +`define STATUS_BP2 24'h000010 +`define STATUS_BP1 24'h000008 +`define STATUS_BP0 24'h000004 +`define STATUS_WEL 24'h000002 +`define STATUS_WIP 24'h000001 + + +`define HLD_RST 23 +`define DRV1 22 +`define DRV0 21 +`define WPS 18 +`define SUS 15 +`define CMPB 14 +`define LB3 13 +`define LB2 12 +`define LB1 11 +`define QE 9 +`define SRL 8 +`define SRP 7 +`define SEC 6 +`define W25Q128JVxIM_TB 5 +`define W25Q128JVxIM_BP2 4 +`define W25Q128JVxIM_BP1 3 +`define W25Q128JVxIM_BP0 2 +`define W25Q128JVxIM_WEL 1 +`define W25Q128JVxIM_WIP 0 + +// Required for specify block +wire flag_quad_mode = status_reg[`QE]; +wire flag_quad_mode_cs = !flag_quad_mode & !CSn; + +specify + + specparam tReset_Suspend_Max = 1000; // Reset / Suspend Granularity. Controls maximum amount of time for model to recognize a Suspend command. + // Also controls amount of time it takes to recognize a reset command + + // CSn timing checks + + specparam tSLCH = 5; + $setup(negedge CSn, posedge CLK, tSLCH, timing_error); + + specparam tCHSL = 5; + $setup(posedge CLK, negedge CSn, tCHSL, timing_error); + + specparam tSHSL_R = 10; + specparam tSHSL_W = 50; + $width(posedge CSn &&& flag_read_op, tSHSL_R, 0, timing_error); + $width(posedge CSn &&& (~flag_read_op), tSHSL_W, 0, timing_error); + + specparam tCHSH = 5; + $setup(posedge CLK, posedge CSn, tCHSH, timing_error); + + specparam tSHCH = 5; + $setup(posedge CSn, posedge CLK, tSHCH, timing_error); + + + // CLK timing checks + + specparam tCYC = 10; // Minimum CLK period for all instructions but READ_PAGE + specparam tCLH = 4; // Minimum CLK high time for all instructions but READ_PAGE + specparam tCLL = 4; // Minimum CLK low time for all instructions but READ_PAGE + $period(posedge CLK &&& (~flag_slow_read), tCYC, timing_error); + $width(posedge CLK &&& (~flag_slow_read), tCLH, 0, timing_error); + $width(negedge CLK &&& (~flag_slow_read), tCLL, 0, timing_error); + + + specparam tCYCR = 20; // Minimum CLK period READ_PAGE + specparam tCRLH = 8; // Minimum CLK high time READ_PAGE + specparam tCRLL = 8; // Minimum CLK low time READ_PAGE + $period(posedge CLK &&& (flag_slow_read), tCYCR, timing_error); + $width(posedge CLK &&& (flag_slow_read), tCRLH, 0, timing_error); + $width(negedge CLK &&& (flag_slow_read), tCRLL, 0, timing_error); + + + // DIO timing checks + + specparam tDVCH = 2; // DIO Data setup time + specparam tCHDX = 5; // DIO Data hold time + + // Make sure to turn off DIO input timing checks when outputing data in dual / quad output mode. + + $setup(DIO, posedge CLK &&& (~DIO_Output_Enable), tDVCH, timing_error); + $hold(posedge CLK, DIO &&& (~DIO_Output_Enable), tCHDX, timing_error); + + // WPn timing checks + + specparam tWHSL = 20; + specparam tSHWL = 100; + + // Make sure to turn off WPn timing checks when outputing data in quad output mode + + // As well, there is no way to detect setup time of CSn and WPn relative to the Write Status Register + // command. Disable timing checks + + //$setup(posedge WPn, negedge CSn &&& (~flag_quad_mode), tWHSL, timing_error); + //$setup(posedge CSn, negedge WPn &&& (~flag_quad_mode), tSHWL, timing_error); + + // HOLDn timing checks + + specparam tHLQZ = 12; + specparam tHHQX = 7; + // specparam tSUS = 20000; + specparam tSUS = 200; // for sim + + + specparam tCHHL = 5; + $setup(posedge CLK, negedge HOLDn &&& (flag_quad_mode_cs),tCHHL, timing_error); + + specparam tHLCH = 5; // Need to verify from datasheet + $hold(posedge CLK, negedge HOLDn &&& (flag_quad_mode_cs),tHLCH, timing_error); + + specparam tCHHH = 5; + $setup(posedge CLK, posedge HOLDn &&& (flag_quad_mode_cs),tCHHH, timing_error); + + specparam tHHCH = 5; + $hold(posedge CLK, posedge HOLDn &&& (flag_quad_mode_cs), tHHCH, timing_error); + + +endspecify + + +parameter tCLQV = 7; // Time to DO output valid. +parameter tSHQZ = 7; // Data output disable time. +parameter tW = 1000; // Write Status Register Write Time +parameter tRES1 = 30000; // Release from power down time 1 +parameter tRES2 = 30000; // Release from power down time 2 +parameter tDP = 3000; // Time for device to enter deep power down. +// parameter tPP = 700000; // Page Program Time +parameter tPP = 700; // just for sim +parameter tSE = 30000000; // Sector Erase Time. +parameter tBE1 = 120000000; // Block Erase Time. 32KB +parameter tBE2 = 150000000; // Block Erase Time. 64KB +parameter tCE_40 = 1000000000; // Chip Erase Time. This constant should be repeated 40 times. + +/****************************************************************************** +The following code is the initialization code run at the beginning of the +simulation. +******************************************************************************/ +initial +begin :initialization + + + // Erase memory array to FFh state. + // for(x = 0; x < (NUM_PAGES * PAGESIZE); x=x+1) + // memory[x] = 8'hff; + // + // dump_mem(); + + $readmemh(`MEM_FILENAME,memory); + $readmemh(`SECSI_FILENAME,secsi); + $readmemh(`SFDP_FILENAME,sfdp); + $readmemh(`SREG_FILENAME,status_reg_otp); + + chip_reset(); + +end + +/****************************************************************************** +The following continuous assignment statement assigns the DIO_Reg register to the +DIO inout wire and so on for the rest of the quad outputs. +******************************************************************************/ + +assign DIO = DIO_Output_Enable_reg ? DIO_Reg : 1'bz; +assign DO = DO_Output_Enable ? DO_Reg : 1'bz; +assign WPn = WPn_Output_Enable_reg ? WPn_Reg : 1'bz; +assign HOLDn = HOLDn_Output_Enable ? HOLDn_Reg : 1'bz; + + +/****************************************************************************** +The following routine occurs when CSn goes low. + +The following routine reads the opcode in from the SPI port and starts command +execution. All commands execute the following flow: + +Command Dispatch ==> Command Protocol Handler ==> Command State Machine (if necessary) + +Whenever CSn goes high, the Command Dispatch and Command Protocol Handler functions +stop execution. The Individual Command State Machines continue to run until +completion, regardless of the state of CSn. + +******************************************************************************/ + +always @(negedge CSn) // When CSn goes low, device becomes active +begin :read_opcode + + flag_read_op_reg = 1'b1; // Assume a read command first. If write, update variable in case statement. + + mode_reg = mode_reg & 8'hf0; + + if((mode_reg & 8'h30) != 8'h20) // If we are in mode 0x20, skip inputing command byte, execute last command + input_byte(cmd_byte); // Read Opcode from SPI Port + + if(!is_qpi(cmd_byte)) + begin + $display("WARNING: Non-QPI command was executed in QPI mode"); + $stop; + end + + if(cmd_byte != `CMD_CHIP_RESET) + flag_enable_reset = 0; // Ensure that ENABLE_RESET immediately precedes CHIP_RESET + + // $display("\nCommand = %h", cmd_byte); + + case (cmd_byte) // Now dispatch the correct function + + + // Mode and Reset commands. + + `CMD_SET_READ_PARAM : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down && flag_qpi_mode) + begin + input_byte(read_param_reg_shadow); + flag_set_read_param = 1; + get_posclk_holdn; + flag_set_read_param = 0; + end + end + + `CMD_ENABLE_QPI : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + if(status_reg[`QE] == 1) + flag_qpi_mode = 1; + end + end + + `CMD_MODE_RESET : + begin + if(!flag_power_down) + flag_qpi_mode = 0; + end + + `CMD_ENABLE_RESET : // Reset can happen at any time. + begin + flag_enable_reset = 1; + end + + `CMD_CHIP_RESET : + begin + if(flag_enable_reset == 1) + begin + flag_reset = 1; + @(posedge CLK); + flag_reset = 0; + end + end + + // Power, ID and Extended Address commands + + `CMD_DEEP_POWERDOWN : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + flag_power_down = 1; + @(posedge CLK); + flag_power_down = 0; + end + end + + + `CMD_READ_SIGNATURE : + begin + if(!status_reg[`W25Q128JVxIM_WIP]) + begin + flag_power_up_exec = 1; + input_byte(null_reg); + input_byte(null_reg); + input_byte(null_reg); + forever + begin + output_byte(DEVICE_ID); + flag_power_up_sig_read = 1; + end + end + end + + `CMD_READ_JEDEC_ID : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + output_byte(MANUFACTURER); + output_byte(JEDEC_ID_HI); + output_byte(JEDEC_ID_LO); + end + end + + `CMD_READ_ID : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + byte_address = 0; + input_byte(byte_address[23:16]); + input_byte(byte_address[15:8]); + input_byte(byte_address[7:0]); + forever + begin + if(byte_address[0]) + begin + output_byte(DEVICE_ID); + output_byte(MANUFACTURER); + end + else + begin + output_byte(MANUFACTURER); + output_byte(DEVICE_ID); + end + end + end + end + + `CMD_READ_ID_DUAL : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + byte_address = 0; + input_byte_dual(byte_address[23:16]); + input_byte_dual(byte_address[15:8]); + input_byte_dual(byte_address[7:0]); + input_byte_dual(mode_reg[7:0]); + forever + begin + if(byte_address[0]) + begin + output_byte_dual(DEVICE_ID); + output_byte_dual(MANUFACTURER); + end + else + begin + output_byte_dual(MANUFACTURER); + output_byte_dual(DEVICE_ID); + end + end + end + end + + `CMD_READ_ID_QUAD : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down && status_reg[`QE]) + begin + byte_address = 0; + input_byte_quad(byte_address[23:16]); + input_byte_quad(byte_address[15:8]); + input_byte_quad(byte_address[7:0]); + input_byte_quad(mode_reg[7:0]); + input_byte_quad(temp[7:0]); + input_byte_quad(temp[7:0]); + forever + begin + if(byte_address[0]) + begin + output_byte_quad(DEVICE_ID); + output_byte_quad(MANUFACTURER); + end + else + begin + output_byte_quad(MANUFACTURER); + output_byte_quad(DEVICE_ID); + end + end + end + end + + `CMD_READ_UNIQUE_ID : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + input_byte(null_reg); + input_byte(null_reg); + input_byte(null_reg); + input_byte(null_reg); + output_byte(UNIQUE_ID[63:56]); + output_byte(UNIQUE_ID[55:48]); + output_byte(UNIQUE_ID[47:40]); + output_byte(UNIQUE_ID[39:32]); + output_byte(UNIQUE_ID[31:24]); + output_byte(UNIQUE_ID[23:16]); + output_byte(UNIQUE_ID[15:8]); + output_byte(UNIQUE_ID[7:0]); + end + end + + + + // Status Register commands + + `CMD_WRITE_ENABLE : + begin + // $display("status0: %d %d %d", flag_power_down, WPn, status_reg[`QE]); + // if((!flag_power_down) && (WPn || status_reg[`QE])) + if(!flag_power_down) begin + status_reg[`W25Q128JVxIM_WEL] = 1; + $display("status_reg[15:8]: %h", status_reg[15:8]); + end + end + + `CMD_WRITE_ENABLE_VSR : + begin + if(!flag_power_down) + flag_volatile_sr_write = 1; + end + + `CMD_WRITE_DISABLE : + begin + if(!flag_power_down) + status_reg[`W25Q128JVxIM_WEL] = 0; + end + + `CMD_READ_STATUS : + begin + if(!flag_power_down) + begin + forever + begin + output_byte(status_reg[7:0]); + end + end + end + + `CMD_READ_STATUS2 : + begin + if(!flag_power_down) + begin + forever + begin + output_byte(status_reg[15:8]); + end + end + end + + `CMD_READ_STATUS3 : + begin + if(!flag_power_down) + begin + forever + begin + output_byte(status_reg[23:16]); + end + end + end + + + `CMD_WRITE_STATUS : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && (status_reg[`W25Q128JVxIM_WEL] || flag_volatile_sr_write) && !flag_power_down && !status_reg[`SUS]) + begin + flag_read_op_reg = 1'b0; + case ({status_reg[`SRL],status_reg[`SRP]}) + 2'b00, 2'b01 : + begin + if((status_reg[`SRP] && WPn) || !status_reg[`SRP] || status_reg[`QE]) + begin + // Zero out high order byte of status register + status_reg_shadow[23:8] = status_reg[23:8]; + input_byte(status_reg_shadow[7:0]); + // flag write now that we have 8 bits....2nd 8 bits is optional + // command must bail if /CS does not go high at 8th or 16th clock + flag_write_status_reg = 1; + // 2nd byte is optional + if(flag_qpi_mode == 1) + @(posedge CLK); + else + get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command read 2nd byte. + flag_write_status_reg = 0; + input_byte_no1stclock(temp); + flag_write_status_reg = 1; + status_reg_shadow[15:8] = temp; + if(flag_qpi_mode == 1) + @(posedge CLK); + else + get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command read 2nd byte. + flag_write_status_reg = 0; + end + end + endcase + end + end + + `CMD_WRITE_STATUS2 : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && (status_reg[`W25Q128JVxIM_WEL] || flag_volatile_sr_write) && !flag_power_down && !status_reg[`SUS]) + begin + $display("`CMD_WRITE_STATUS2"); + flag_read_op_reg = 1'b0; + case ({status_reg[`SRL],status_reg[`SRP]}) + 2'b00, 2'b01 : + begin + if((status_reg[`SRP] && WPn) || !status_reg[`SRP] || status_reg[`QE]) + begin + status_reg_shadow[23:16] = status_reg[23:16]; + status_reg_shadow[7:0] = status_reg[7:0]; + input_byte(status_reg_shadow[15:8]); + // flag write now that we have 8 bits.... + // command must bail if /CS does not go high at 8th clock + flag_write_status_reg = 1; + $display("status_reg_shadow[15:8]: %h", status_reg_shadow[15:8]); + // 2nd byte is optional + if(flag_qpi_mode == 1) + @(posedge CLK); + else + get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command read 2nd byte. + flag_write_status_reg = 0; + end + end + endcase + end + end + + `CMD_WRITE_STATUS3 : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && (status_reg[`W25Q128JVxIM_WEL] || flag_volatile_sr_write) && !flag_power_down && !status_reg[`SUS]) + begin + flag_read_op_reg = 1'b0; + case ({status_reg[`SRL],status_reg[`SRP]}) + 2'b00, 2'b01 : + begin + if((status_reg[`SRP] && WPn) || !status_reg[`SRP] || status_reg[`QE]) + begin + status_reg_shadow[15:0] = status_reg[15:0]; + input_byte(status_reg_shadow[23:16]); + // flag write now that we have 8 bits.... + // command must bail if /CS does not go high at 8th clock + flag_write_status_reg = 1; + // 2nd byte is optional + if(flag_qpi_mode == 1) + @(posedge CLK); + else + get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command read 2nd byte. + flag_write_status_reg = 0; + end + end + endcase + end + end + + + // Write Page Commands + + `CMD_PAGE_PROGRAM : + begin + // $display("status1: %d %d %d", status_reg[`W25Q128JVxIM_WEL], !status_reg[`SUS], !flag_power_down); + if(status_reg[`W25Q128JVxIM_WEL] && !status_reg[`SUS] && !flag_power_down) + begin + // $display("status"); + flag_read_op_reg = 1'b0; + write_page(0); + end + end + + `CMD_PAGE_PROGRAM_QUAD : + begin + $display("CMD_PAGE_PROGRAM_QUAD: %h %h %h %h", status_reg[`W25Q128JVxIM_WEL], status_reg[`SUS], status_reg[`QE], flag_power_down); + if(status_reg[`W25Q128JVxIM_WEL]&& !status_reg[`SUS] && status_reg[`QE] && !flag_power_down) + begin + flag_read_op_reg = 1'b0; + $display("CMD_PAGE_PROGRAM_QUAD2"); + write_page(1); + end + end + + // Suspend / Resume Commands + + `CMD_SUSPEND : + begin + if(!flag_power_down && !flag_erase_bulk) + begin + if((!flag_suspend) && (flag_erase_sector || flag_erase_secsi_sector || flag_erase_half_block || flag_erase_block || flag_prog_page || flag_prog_secsi_page)) + begin + flag_suspend = 1'b1; + get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command. + flag_suspend = 1'b0; + end + end + end + + `CMD_RESUME : + begin + if(!flag_power_down) + begin + if(flag_suspend) + begin + flag_resume = 1'b1; + get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command. + flag_resume = 1'b0; + end + end + end + + // Erase Commands + + `CMD_SECTOR_ERASE : + begin + if(status_reg[`W25Q128JVxIM_WEL] && !flag_power_down && !status_reg[`SUS]) + begin + flag_read_op_reg = 1'b0; + + input_byte(byte_address[23:16]); + input_byte(byte_address[15:8]); + input_byte(byte_address[7:0]); + if(!write_protected(byte_address)) + begin + flag_erase_sector = 1; + get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command. + flag_erase_sector = 0; + end + end + end + + `CMD_HALF_BLOCK_ERASE : + begin + if(status_reg[`W25Q128JVxIM_WEL] && !flag_power_down && !status_reg[`SUS]) + begin + flag_read_op_reg = 1'b0; + + input_byte(byte_address[23:16]); + input_byte(byte_address[15:8]); + input_byte(byte_address[7:0]); + if(!write_protected(byte_address)) + begin + flag_erase_half_block = 1; + get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command. + flag_erase_half_block = 0; + end + end + end + + `CMD_BLOCK_ERASE : + begin + if(status_reg[`W25Q128JVxIM_WEL] && !flag_power_down && !status_reg[`SUS]) + begin + flag_read_op_reg = 1'b0; + + input_byte(byte_address[23:16]); + input_byte(byte_address[15:8]); + input_byte(byte_address[7:0]); + if(!write_protected(byte_address)) + begin + flag_erase_block = 1; + get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command. + flag_erase_block = 0; + end + end + end + + `CMD_BULK_ERASE, `CMD_BULK_ERASE2 : + begin + if(status_reg[`W25Q128JVxIM_WEL] && !flag_power_down && !status_reg[`SUS]) + begin + flag_read_op_reg = 1'b0; + case ({status_reg[`W25Q128JVxIM_BP0],status_reg[`W25Q128JVxIM_BP1]}) + 2'b00 : + begin + flag_erase_bulk = 1; + get_posclk_holdn; + flag_erase_bulk = 0; + end + endcase + end + end + + // Read Page Commands + + `CMD_READ_DATA : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + flag_slow_read_reg = 1'b1; + input_byte(byte_address[23:16]); + input_byte(byte_address[15:8]); + input_byte(byte_address[7:0]); + forever + begin + byte_address = byte_address & ADDRESS_MASK; + output_byte(memory[byte_address]); + byte_address = byte_address + 1; + end + end + end + + `CMD_READ_DATA_FAST : + begin + if(!flag_power_down) + read_page(1,0); + end + + + `CMD_READ_DATA_FAST_DTR : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + + input_byte_DTR(byte_address[23:16]); + input_byte_DTR(byte_address[15:8]); + input_byte_DTR(byte_address[7:0]); + + for(x = 5; x >= 0; x=x-1) + begin + get_posclk_holdn; + null_reg[x] = DIO; + end + + forever + begin + byte_address = byte_address & ADDRESS_MASK; + output_byte_DTR(memory[byte_address]); + byte_address = byte_address + 1; + end + end + end + + `CMD_READ_DATA_FAST_DUAL_IO_DTR : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + + input_byte_dual_DTR(byte_address[23:16]); + input_byte_dual_DTR(byte_address[15:8]); + input_byte_dual_DTR(byte_address[7:0]); + input_byte_dual_DTR(mode_reg[7:0]); + + for(x = 3; x >= 0; x=x-1) + begin + get_posclk_holdn; + null_reg[x] = DIO; + end + + forever + begin + byte_address = byte_address & ADDRESS_MASK; + output_byte_dual_DTR(memory[byte_address]); + byte_address = byte_address + 1; + end + end + end + + `CMD_READ_DATA_FAST_QUAD_IO_DTR : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down && status_reg[`QE]) + begin + + input_byte_quad_DTR(byte_address[23:16]); + input_byte_quad_DTR(byte_address[15:8]); + input_byte_quad_DTR(byte_address[7:0]); + input_byte_quad_DTR(mode_reg[7:0]); + + for(x = 6; x >= 0; x=x-1) + begin + get_posclk_holdn; + null_reg[x] = DIO; + end + + forever + begin + byte_address = byte_address & ADDRESS_MASK; + output_byte_quad_DTR(memory[byte_address]); + byte_address = byte_address + 1; + end + end + end + + `CMD_READ_DATA_FAST_DTR_WRAP : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + + input_byte_quad_DTR(byte_address[23:16]); + input_byte_quad_DTR(byte_address[15:8]); + input_byte_quad_DTR(byte_address[7:0]); + input_byte_quad_DTR(null_reg); + + forever + begin + byte_address = byte_address & ADDRESS_MASK; + output_byte_quad_DTR(memory[byte_address]); + case ({read_param_reg[1],read_param_reg[0]}) + 2'b00 : + byte_address[2:0] = byte_address[2:0] + 1; + 2'b01 : + byte_address[3:0] = byte_address[3:0] + 1; + 2'b10 : + byte_address[4:0] = byte_address[4:0] + 1; + 2'b11 : + byte_address[5:0] = byte_address[5:0] + 1; + endcase + end + end + end + + `CMD_READ_DATA_FAST_WRAP : + begin + if(!flag_power_down) + begin + if(flag_qpi_mode) + read_page_quadio(cmd_byte); + end + end + + `CMD_READ_DATA_FAST_DUAL : + begin + if(!flag_power_down) + read_page(2,0); + end + + `CMD_READ_DATA_FAST_QUAD : + begin + if(!flag_power_down && status_reg[`QE]) + read_page(3,0); + end + + `CMD_READ_DATA_FAST_DUAL_IO : + begin + if(!flag_power_down) + read_page_dualio; + end + + `CMD_READ_DATA_FAST_QUAD_IO : + begin + if(!flag_power_down && status_reg[`QE]) + read_page_quadio(cmd_byte); + end + + `CMD_SET_BURST_WRAP : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down && status_reg[`QE]) + begin + input_byte_quad(temp[7:0]); + input_byte_quad(temp[7:0]); + input_byte_quad(temp[7:0]); + input_byte_quad(wrap_reg[7:0]); + end + end + + `CMD_READ_SFDP : + begin + if(!flag_power_down) + begin + flag_slow_read_reg = 1'b1; + read_page(0,2); + end + end + + + // Security Register Opcodes + + `CMD_SREG_READ : + begin + if(!flag_power_down) + begin + read_page(0,1); + end + end + + `CMD_SREG_ERASE : + begin + if(status_reg[`W25Q128JVxIM_WEL] && !flag_power_down && !status_reg[`SUS]) + begin + flag_read_op_reg = 1'b0; + input_byte(byte_address[23:16]); + input_byte(byte_address[15:8]); + input_byte(byte_address[7:0]); + case (byte_address[23:8]) + 16'h10 : + begin + if(!status_reg[`LB1]) + begin + flag_erase_secsi_sector = 1; + get_posclk_holdn; + flag_erase_secsi_sector = 0; + end + end + 16'h20 : + begin + if(!status_reg[`LB2]) + begin + flag_erase_secsi_sector = 1; + get_posclk_holdn; + flag_erase_secsi_sector = 0; + end + end + 16'h30 : + begin + if(!status_reg[`LB3]) + begin + flag_erase_secsi_sector = 1; + get_posclk_holdn; + flag_erase_secsi_sector = 0; + end + end + + endcase + end + end + + `CMD_SREG_PROGRAM : + begin + if(status_reg[`W25Q128JVxIM_WEL] && !flag_power_down && !status_reg[`SUS]) + begin + flag_read_op_reg = 1'b0; + begin + if(!status_reg[`W25Q128JVxIM_WIP]) + begin + input_byte(prog_byte_address[23:16]); + input_byte(prog_byte_address[15:8]); + input_byte(prog_byte_address[7:0]); + + case (byte_address[23:8]) + 16'h10 : + if(!status_reg[`LB1]) + fill_page_latch(0,prog_byte_address,1); + 16'h20 : + if(!status_reg[`LB2]) + fill_page_latch(0,prog_byte_address,1); + 16'h30 : + if(!status_reg[`LB3]) + fill_page_latch(0,prog_byte_address,1); + endcase + end + end + end + end + + // Lock Bit commands + + `CMD_GLOBAL_BLOCK_LOCK : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + for(x = 0; x < NUM_LOCKBITS; x=x+1) + lock_array[x] = 1; + end + end + + `CMD_GLOBAL_BLOCK_UNLOCK : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + for(x = 0; x < NUM_LOCKBITS; x=x+1) + lock_array[x] = 0; + end + end + + `CMD_INDIVIDUAL_LOCK : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + + input_byte(byte_address[23:16]); + input_byte(byte_address[15:8]); + input_byte(byte_address[7:0]); + + lock_array[lockbit_index(byte_address)] = 1; + end + end + + `CMD_INDIVIDUAL_UNLOCK : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + input_byte(byte_address[23:16]); + input_byte(byte_address[15:8]); + input_byte(byte_address[7:0]); + + lock_array[lockbit_index(byte_address)] = 0; + end + end + + `CMD_READ_BLOCK_LOCK : + begin + if(!status_reg[`W25Q128JVxIM_WIP] && !flag_power_down) + begin + input_byte(byte_address[23:16]); + input_byte(byte_address[15:8]); + input_byte(byte_address[7:0]); + + null_reg[7:1] = 0; + null_reg[0] = lock_array[lockbit_index(byte_address)]; + output_byte(null_reg); + end + end + + default : + begin + $display("Invalid Opcode. (%0h)",cmd_byte); + // $stop; + end + endcase +end + + +/****************************************************************************** + The following routine occurs when CSn goes high. In the case, all communications + activities inside the chip must halt. Transfer and Program/Erase conditions + will continue. +******************************************************************************/ + +always @(posedge CSn) // When CSn goes high, device becomes in-active +begin :disable_interface + #tSHQZ; // Data-output disable time + HOLDn_Active = 1'b0; // Disable HOLDn from mucking with output registers. + DO_Output_Enable = 1'b0; // Tri-state DO output. + DIO_Output_Enable_reg = 1'b0; // Tri-state DIO output. + WPn_Output_Enable_reg = 1'b0; // Tri-state WPn output + HOLDn_Output_Enable = 1'b0; // Tri-state HOLDn output + flag_slow_read_reg = 1'b0; // Initiate normal timing checks + + disable input_byte; + disable input_byte_dual; + disable input_mode_dual; + disable input_byte_quad; + disable output_byte; + disable output_byte_dual; + disable output_byte_quad; + disable read_opcode; + disable write_page; + disable fill_page_latch; + disable read_page; + disable read_page_dualio; + disable read_page_quadio; + disable get_posclk_holdn; + disable get_negclk_holdn; +end + +/****************************************************************************** + The following routine occurs when HOLDn goes low. +******************************************************************************/ + +always @(negedge (HOLDn & !status_reg[`QE] & !CSn)) +begin + + if(!HOLDn) + begin + #tHLQZ; + temp_DIO_Output_Enable_reg = DIO_Output_Enable_reg; + temp_DO_Output_Enable = DO_Output_Enable; + DIO_Output_Enable_reg = 1'b0; // Set DIO and DO to an input state + DO_Output_Enable = 1'b0; + HOLDn_Active = 1'b1; + end + + +end + +/****************************************************************************** + The following routine occurs when HOLDn goes high. +******************************************************************************/ + +always @(posedge HOLDn) +begin + + if(HOLDn_Active == 1'b1) + begin + #tHHQX; + DIO_Output_Enable_reg = temp_DIO_Output_Enable_reg; + DO_Output_Enable = temp_DO_Output_Enable; + HOLDn_Active = 1'b0; + end + +end + +/****************************************************************************** + GENERAL PURPOSE SUBROUTINES +******************************************************************************/ + + +task chip_reset; + integer x; + begin + + // Tri-state all outputs + temp_DIO_Output_Enable_reg = 1'b0; + DIO_Output_Enable_reg = 1'b0; // Tri-state DIO Output + temp_DO_Output_Enable = 1'b0; + DO_Output_Enable = 1'b0; // Tri-state DO Output. + WPn_Output_Enable_reg = 1'b0; // Tri-state WPn Output + HOLDn_Output_Enable = 1'b0; // Tri-state HOLDn Output + HOLDn_Active = 1'b0; + + // Set all output registers to default to 0 + + DIO_Reg = 1'b0; + DO_Reg = 1'b0; + WPn_Reg = 1'b0; + HOLDn_Reg = 1'b0; + + mode_reg = 8'h00; // Setup null mode register + wrap_reg = 8'b00010000; // Setup Bit 4 of wrap register to 1 as default + read_param_reg = 8'h00; // Reset defaults for read parameter register + + + status_reg = 0; // Status register default OTP values. + status_reg[`QE] = status_reg_otp[`QE]; + status_reg[`SRL] = status_reg_otp[`SRL]; + status_reg[`SRP] = status_reg_otp[`SRP]; + status_reg[`W25Q128JVxIM_BP0] = status_reg_otp[`W25Q128JVxIM_BP0]; + status_reg[`W25Q128JVxIM_BP1] = status_reg_otp[`W25Q128JVxIM_BP1]; + status_reg[`W25Q128JVxIM_BP2] = status_reg_otp[`W25Q128JVxIM_BP2]; + status_reg[`SEC] = status_reg_otp[`SEC]; + status_reg[`W25Q128JVxIM_TB] = status_reg_otp[`W25Q128JVxIM_TB]; + status_reg[`CMPB] = status_reg_otp[`CMPB]; + status_reg[`WPS] = status_reg_otp[`WPS]; + status_reg[`DRV0] = status_reg_otp[`DRV0]; + status_reg[`DRV1] = status_reg_otp[`DRV1]; + status_reg[`HLD_RST] = status_reg_otp[`HLD_RST]; + status_reg[`LB1] = status_reg_otp[`LB1]; + status_reg[`LB2] = status_reg_otp[`LB2]; + status_reg[`LB3] = status_reg_otp[`LB3]; + + flag_prog_page = 0; + flag_prog_secsi_page = 0; + flag_erase_sector = 0; + flag_erase_half_block = 0; + flag_erase_block = 0; + flag_erase_secsi_sector = 0; + flag_erase_bulk = 0; + flag_power_down = 0; + flag_power_up_exec = 0; + flag_power_up_sig_read = 0; + flag_write_status_reg = 0; + flag_slow_read_reg = 1'b0; // Flag for standard read command....i.e. different timings + flag_read_op_reg = 1'b0; // Flag for any read command....i.e. different timings. + flag_suspend = 1'b0; // clear erase suspend status + flag_suspend_enabled = 1'b0; + flag_volatile_sr_write = 1'b0; + flag_qpi_mode = 0; + flag_enable_reset = 0; + flag_reset = 0; + flag_reset_condition = 0; + flag_set_read_param = 0; + timing_error = 0; + cmd_byte = 0; + null_reg = 0; + in_byte = 0; + out_byte = 0; + + // Reset the Lock Array to full write protect + + for(x = 0; x < NUM_LOCKBITS; x=x+1) + lock_array[x] = 1; + + end +endtask + + +/****************************************************************************** + The following routine will input 1 byte from the SPI DIO pin and place + the results in the input_data register. +******************************************************************************/ + +task input_byte; + output [7:0] input_data; + integer x; + begin + + if(flag_qpi_mode == 1) + input_byte_quad(input_data); + else + begin + // Set the DIO output register high-Z + if(DIO_Output_Enable_reg != 1'b0) + DIO_Output_Enable_reg = 1'b0; + + // $display("%t", $time); + for(x = 7; x >= 0; x=x-1) + begin + get_posclk_holdn; + input_data[x] = DIO; + end + in_byte = input_data; + end + end +endtask + +/****************************************************************************** + The following routine will input 1 byte from the SPI DIO pin and place + the results in the input_data register in DTR mode. +******************************************************************************/ + +task input_byte_DTR; + output [7:0] input_data; + integer x; + begin + + if(flag_qpi_mode == 1) + input_byte_quad(input_data); + else + begin + // Set the DIO output register high-Z + if(DIO_Output_Enable_reg != 1'b0) + DIO_Output_Enable_reg = 1'b0; + + for(x = 7; x >= 0; x=x-2) + begin + get_posclk_holdn; + input_data[x] = DIO; + get_negclk_holdn; + input_data[x-1] = DIO; + end + in_byte = input_data; + end + end +endtask + +/****************************************************************************** + The following routine will input 1 byte from the SPI DIO pin and place + the results in the input_data register. +******************************************************************************/ + +task input_byte_no1stclock; + output [7:0] input_data; + integer x; + begin + + if(flag_qpi_mode == 1) + input_byte_quad_no1stclock(input_data); + else + begin + // Set the DIO output register high-Z + if(DIO_Output_Enable_reg != 1'b0) + DIO_Output_Enable_reg = 1'b0; + + for(x = 7; x >= 0; x=x-1) + begin + if(x != 7) + get_posclk_holdn; + input_data[x] = DIO; + end + in_byte = input_data; + end + end +endtask + +/****************************************************************************** + The following routine will input 1 byte from the SPI DIO pin and DO pin and place + the results in the input_data register. +******************************************************************************/ + +task input_byte_dual; + output [7:0] input_data; + integer x; + begin + + // Set the DIO output register high-Z + if(DIO_Output_Enable_reg != 1'b0) + DIO_Output_Enable_reg = 1'b0; + + // Set the DO output register high-Z + if(DO_Output_Enable != 1'b0) + DO_Output_Enable = 1'b0; + + for(x = 7; x >= 0; x=x-2) + begin + get_posclk_holdn; + + input_data[x-1] = DIO; + input_data[x] = DO; + end + in_byte = input_data; + end +endtask + +/****************************************************************************** + The following routine will input 1 byte from the SPI DIO pin and DO pin and place + the results in the input_data register in DTR mode. +******************************************************************************/ + +task input_byte_dual_DTR; + output [7:0] input_data; + integer x; + begin + + // Set the DIO output register high-Z + if(DIO_Output_Enable_reg != 1'b0) + DIO_Output_Enable_reg = 1'b0; + + // Set the DO output register high-Z + if(DO_Output_Enable != 1'b0) + DO_Output_Enable = 1'b0; + + for(x = 7; x >= 0; x=x-4) + begin + get_posclk_holdn; + input_data[x-1] = DIO; + input_data[x] = DO; + get_negclk_holdn; + input_data[x-3] = DIO; + input_data[x-2] = DO; + end + in_byte = input_data; + end +endtask + +/****************************************************************************** + The following routine will input 1 nibble from the SPI DIO pin and DO pin and place + the results in the input_data register. +******************************************************************************/ + +task input_mode_dual; + output [5:0] input_data; + integer x; + begin + + // Set the DIO output register high-Z + if(DIO_Output_Enable_reg != 1'b0) + DIO_Output_Enable_reg = 1'b0; + + // Set the DO output register high-Z + if(DO_Output_Enable != 1'b0) + DO_Output_Enable = 1'b0; + + for(x = 5; x >= 0; x=x-2) + begin + get_posclk_holdn; + + input_data[x-1] = DIO; + input_data[x] = DO; + end + + end +endtask + +/****************************************************************************** + The following routine will input 1 byte from the SPI DIO and DO and WPn and HOLDn pins + and place the results in the input_data register. +******************************************************************************/ + +task input_byte_quad; + output [7:0] input_data; + integer x; + begin + + // Set the DIO output register high-Z + if(DIO_Output_Enable_reg != 1'b0) + DIO_Output_Enable_reg = 1'b0; + + // Set the DO output register high-Z + if(DO_Output_Enable != 1'b0) + DO_Output_Enable = 1'b0; + + // Set the WPn output register high-Z + if(WPn_Output_Enable_reg != 1'b0) + WPn_Output_Enable_reg = 1'b0; + + // Set the HOLDn output register high-Z + if(HOLDn_Output_Enable != 1'b0) + DO_Output_Enable = 1'b0; + + for(x = 7; x >= 0; x=x-4) + begin + @(posedge CLK); + input_data[x-3] = DIO; + input_data[x-2] = DO; + input_data[x-1] = WPn; + input_data[x] = HOLDn; + end + in_byte = input_data; + end +endtask + +/****************************************************************************** + The following routine will input 1 byte from the SPI DIO and DO and WPn and HOLDn pins + and place the results in the input_data register in DTR mode. +******************************************************************************/ + +task input_byte_quad_DTR; + output [7:0] input_data; + integer x; + begin + + // Set the DIO output register high-Z + if(DIO_Output_Enable_reg != 1'b0) + DIO_Output_Enable_reg = 1'b0; + + // Set the DO output register high-Z + if(DO_Output_Enable != 1'b0) + DO_Output_Enable = 1'b0; + + // Set the WPn output register high-Z + if(WPn_Output_Enable_reg != 1'b0) + WPn_Output_Enable_reg = 1'b0; + + // Set the HOLDn output register high-Z + if(HOLDn_Output_Enable != 1'b0) + DO_Output_Enable = 1'b0; + + for(x = 7; x >= 0; x=x-8) + begin + @(posedge CLK); + input_data[x-3] = DIO; + input_data[x-2] = DO; + input_data[x-1] = WPn; + input_data[x] = HOLDn; + @(negedge CLK); + input_data[x-7] = DIO; + input_data[x-6] = DO; + input_data[x-5] = WPn; + input_data[x-4] = HOLDn; + end + in_byte = input_data; + end +endtask + +/****************************************************************************** + The following routine will input 1 byte from the SPI DIO and DO and WPn and HOLDn pins + and place the results in the input_data register. +******************************************************************************/ + +task input_byte_quad_no1stclock; + output [7:0] input_data; + integer x; + begin + + // Set the DIO output register high-Z + if(DIO_Output_Enable_reg != 1'b0) + DIO_Output_Enable_reg = 1'b0; + + // Set the DO output register high-Z + if(DO_Output_Enable != 1'b0) + DO_Output_Enable = 1'b0; + + // Set the WPn output register high-Z + if(WPn_Output_Enable_reg != 1'b0) + WPn_Output_Enable_reg = 1'b0; + + // Set the HOLDn output register high-Z + if(HOLDn_Output_Enable != 1'b0) + DO_Output_Enable = 1'b0; + + for(x = 7; x >= 0; x=x-4) + begin + if(x != 7) + @(posedge CLK); + input_data[x-3] = DIO; + input_data[x-2] = DO; + input_data[x-1] = WPn; + input_data[x] = HOLDn; + end + in_byte = input_data; + end +endtask + + +/****************************************************************************** + The following routine will output 1 byte to the DO pin. +******************************************************************************/ + +task output_byte; + input [7:0] output_data; + integer x; + begin + + if(flag_qpi_mode == 1) + output_byte_quad(output_data); + else + begin + out_byte = output_data; + for(x = 7; x >= 0; x=x-1) + begin + get_negclk_holdn; + + if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. + DO_Output_Enable = 1'b1; + #tCLQV DO_Reg = output_data[x]; + end + end + end +endtask + +/****************************************************************************** + The following routine will output 1 byte to the DO pin in DTR mode. +******************************************************************************/ + +task output_byte_DTR; + input [7:0] output_data; + integer x; + begin + + if(flag_qpi_mode == 1) + output_byte_quad(output_data); + else + begin + out_byte = output_data; + for(x = 7; x >= 0; x=x-2) + begin + get_negclk_holdn; + + if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. + DO_Output_Enable = 1'b1; + #tCLQV DO_Reg = output_data[x]; + get_posclk_holdn; + + if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. + DO_Output_Enable = 1'b1; + #tCLQV DO_Reg = output_data[x-1]; + end + end + end +endtask + +/****************************************************************************** + The following routine will output 1 byte to the DO and DIO pin. +******************************************************************************/ + +task output_byte_dual; + input [7:0] output_data; + integer x; + begin + out_byte = output_data; + for(x = 7; x >= 0; x=x-2) + begin + get_negclk_holdn; + + if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. + DO_Output_Enable = 1'b1; + if(DIO_Output_Enable_reg == 1'b0) + DIO_Output_Enable_reg = 1'b1; + + #tCLQV ; + DIO_Reg = output_data[x-1]; + DO_Reg = output_data[x]; + end + end +endtask + +/****************************************************************************** + The following routine will output 1 byte to the DO and DIO pin in DTR mode. +******************************************************************************/ + +task output_byte_dual_DTR; + input [7:0] output_data; + integer x; + begin + out_byte = output_data; + for(x = 7; x >= 0; x=x-4) + begin + get_negclk_holdn; + + if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. + DO_Output_Enable = 1'b1; + if(DIO_Output_Enable_reg == 1'b0) + DIO_Output_Enable_reg = 1'b1; + + #tCLQV ; + DIO_Reg = output_data[x-1]; + DO_Reg = output_data[x]; + + get_posclk_holdn; + + if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. + DO_Output_Enable = 1'b1; + if(DIO_Output_Enable_reg == 1'b0) + DIO_Output_Enable_reg = 1'b1; + + #tCLQV ; + DIO_Reg = output_data[x-3]; + DO_Reg = output_data[x-2]; + end + end +endtask + +/****************************************************************************** + The following routine will output 1 byte to the DO and DIO and WPn and HOLDn pin. +******************************************************************************/ + +task output_byte_quad; + input [7:0] output_data; + integer x; + begin + out_byte = output_data; + for(x = 7; x >= 0; x=x-4) + begin + @(negedge CLK); + if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. + DO_Output_Enable = 1'b1; + if(DIO_Output_Enable_reg == 1'b0) + DIO_Output_Enable_reg = 1'b1; + if(WPn_Output_Enable_reg == 1'b0) + WPn_Output_Enable_reg = 1'b1; + if(HOLDn_Output_Enable == 1'b0) + HOLDn_Output_Enable = 1'b1; + + #tCLQV; + DIO_Reg = output_data[x-3]; + DO_Reg = output_data[x-2]; + WPn_Reg = output_data[x-1]; + HOLDn_Reg = output_data[x]; + end + end +endtask + +/****************************************************************************** + The following routine will output 1 byte to the DO and DIO and WPn and HOLDn pin in DTR mode. +******************************************************************************/ + +task output_byte_quad_DTR; + input [7:0] output_data; + integer x; + begin + out_byte = output_data; + for(x = 7; x >= 0; x=x-8) + begin + @(negedge CLK); + if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. + DO_Output_Enable = 1'b1; + if(DIO_Output_Enable_reg == 1'b0) + DIO_Output_Enable_reg = 1'b1; + if(WPn_Output_Enable_reg == 1'b0) + WPn_Output_Enable_reg = 1'b1; + if(HOLDn_Output_Enable == 1'b0) + HOLDn_Output_Enable = 1'b1; + + #tCLQV; + DIO_Reg = output_data[x-3]; + DO_Reg = output_data[x-2]; + WPn_Reg = output_data[x-1]; + HOLDn_Reg = output_data[x]; + + @(posedge CLK); + if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. + DO_Output_Enable = 1'b1; + if(DIO_Output_Enable_reg == 1'b0) + DIO_Output_Enable_reg = 1'b1; + if(WPn_Output_Enable_reg == 1'b0) + WPn_Output_Enable_reg = 1'b1; + if(HOLDn_Output_Enable == 1'b0) + HOLDn_Output_Enable = 1'b1; + + #tCLQV; + DIO_Reg = output_data[x-7]; + DO_Reg = output_data[x-6]; + WPn_Reg = output_data[x-5]; + HOLDn_Reg = output_data[x-4]; + end + end +endtask + +/****************************************************************************** + The following routine will return when a negative edge happens on CLK with respect + to the HOLDn signal. +******************************************************************************/ + +task get_negclk_holdn; + begin + + if(status_reg[`QE]) // Quad bus is enabled, HOLD condition does not exist + @(negedge CLK); // Therefore return negedge CLK + else + @(negedge (CLK & HOLDn)); // If Quad bus is disabled, return CLK only when HOLDn is high. + + end +endtask + +/****************************************************************************** + The following routine will return when a positive edge happens on CLK with respect + to the HOLDn signal. +******************************************************************************/ + +task get_posclk_holdn; + begin + + if(status_reg[`QE]) // Quad bus is enabled, HOLD condition does not exist + @(posedge CLK); // Therefore return negedge CLK + else + @(posedge (CLK & HOLDn)); // If Quad bus is disabled, return CLK only when HOLDn is high. + + end +endtask + +/****************************************************************************** + The following routine will delay the specified amount of time while waiting for the + flag_suspend_enabled signal or the flag_reset_condition signal. If the flag_suspend_enable asserts, the delay routine will wait indefinetly + until flag_suspend_enable deasserts; if flag_reset_condition asserts, the routine exits immediately +******************************************************************************/ + +task wait_reset_suspend; + input [31:0] delay; + integer waitx; + integer num_iterations; + begin + + // Warning, this routine is not recursive and cannot be called while suspend is enabled! + + // This function delays while optionally waiting for the suspend signal. To reduce CPU simulation resources, + // This routine will count in tReset_Suspend_Max increments down to a value less than tReset_Suspend_Max wait and then + // look every nanosecond to finish. It exists under any circumstance if the chip is reset. + + waitx = 0; + + if(delay >= tReset_Suspend_Max) + begin + num_iterations = delay / tReset_Suspend_Max; + for(waitx = 0; waitx < num_iterations; waitx=waitx+1) // check for erase suspend while part is programming + begin + if(flag_reset_condition) // If chip is reset, exit loop + waitx = num_iterations; // Break the loop + else + begin + wait(!flag_suspend_enabled || flag_reset_condition); + #tReset_Suspend_Max; + end + end + end + + num_iterations = delay % tReset_Suspend_Max; + for(waitx = 0; waitx < num_iterations; waitx=waitx+1) + begin + if(flag_reset_condition) // check for chip reset while part is programming + waitx = num_iterations; + else + begin + wait(!flag_suspend_enabled || flag_reset_condition); // check for erase suspend while waiting + #1; + end + end + end +endtask + +/****************************************************************************** + The following routine will delay the specified amount of time while waiting for the + flag_reset_condition signal. If flag_reset_condition is asserted, the routine aborts because the chip has been reset +******************************************************************************/ + +task wait_reset; + input [31:0] delay; + integer waitx; + integer num_iterations; + begin + + // This task delays while waiting for the reset signal. To reduce CPU simulation resources, + // This routine will count in tReset_Suspend_Max increments down to a value less than tReset_Suspend_Max wait and then + // look every nanosecond to finish. It exists under any circumstance if the chip is reset. + + waitx = 0; + + if(delay >= tReset_Suspend_Max) + begin + num_iterations = delay / tReset_Suspend_Max; + for(waitx = 0; waitx < num_iterations; waitx=waitx+1) // check for erase suspend while part is programming + begin + if(flag_reset_condition) // If chip is reset, exit loop + waitx = num_iterations; // Break the loop + else + #tReset_Suspend_Max; // else delay + end + end + + num_iterations = delay % tReset_Suspend_Max; + for(waitx = 0; waitx < num_iterations; waitx=waitx+1) // check for erase suspend while part is programming + begin + if(flag_reset_condition) // check for chip reset while part is programming + waitx = num_iterations; + else + #1; + end + end +endtask + + + +/****************************************************************************** + The following function returns whether or not the current page is write + protected based upon the status register protect bits. +******************************************************************************/ + +function write_protected; + input [23:0] byte_address; + begin + + if(status_reg[`WPS]) + write_protected = lock_array[lockbit_index(byte_address)]; + else + begin + casez ({status_reg[`SEC], status_reg[`W25Q128JVxIM_TB],status_reg[`W25Q128JVxIM_BP2],status_reg[`W25Q128JVxIM_BP1],status_reg[`W25Q128JVxIM_BP0]}) + 5'b??000 : + write_protected = 1'b0 ^ status_reg[`CMPB]; + 5'b00001 : + begin + if(byte_address >= (NUM_PAGES * 63 / 64 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + 5'b00010 : + begin + if(byte_address >= (NUM_PAGES * 31 / 32 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + 5'b00011 : + begin + if(byte_address >= (NUM_PAGES * 15 / 16 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + + 5'b00100 : + begin + if(byte_address >= (NUM_PAGES * 7 / 8 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + 5'b00101 : + begin + if(byte_address >= (NUM_PAGES * 3 / 4 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + 5'b00110 : + begin + if(byte_address >= (NUM_PAGES * 1 / 2 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + + 5'b01001 : + begin + if(byte_address < (NUM_PAGES * 1 / 64 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + + 5'b01010 : + begin + if(byte_address < (NUM_PAGES * 1 / 32 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + + 5'b01011 : + begin + if(byte_address < (NUM_PAGES * 1 / 16 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + + 5'b01100: + begin + if(byte_address < (NUM_PAGES * 1 / 8 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + + 5'b01101 : + begin + if(byte_address < (NUM_PAGES * 1 / 4 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + + 5'b01110 : + begin + if(byte_address < (NUM_PAGES * 1 / 2 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + + 5'b10001 : + begin + if(byte_address >= (NUM_PAGES * 4095 / 4096 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + 5'b10010 : + begin + if(byte_address >= (NUM_PAGES * 2047 / 2048 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + 5'b10011 : + begin + if(byte_address >= (NUM_PAGES * 1023 / 1024 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + + 5'b1010? : + begin + if(byte_address >= (NUM_PAGES * 511 / 512 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + + 5'b11001 : + begin + if(byte_address < (NUM_PAGES * 1 / 4096 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + 5'b11010 : + begin + if(byte_address < (NUM_PAGES * 1 / 2048 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + 5'b11011 : + begin + if(byte_address < (NUM_PAGES * 1 / 1024 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + + 5'b1110? : + begin + if(byte_address < (NUM_PAGES * 1 / 512 * PAGESIZE)) + write_protected = 1'b1 ^ status_reg[`CMPB]; + else + write_protected = 1'b0 ^ status_reg[`CMPB]; + end + + 5'b??111 : + begin + write_protected = 1'b1 ^ status_reg[`CMPB]; + end + endcase + end + end +endfunction + + +/****************************************************************************** + The following function returns whether passed command is valid in QPI mode. + ******************************************************************************/ + +function is_qpi; + input [7:0] cmd_byte; + begin + + if(flag_qpi_mode == 1) + begin + case (cmd_byte) // check command byte for being valid in QPI mode. + + `CMD_WRITE_ENABLE, `CMD_WRITE_ENABLE_VSR, `CMD_WRITE_DISABLE, `CMD_READ_STATUS, + `CMD_READ_STATUS2, `CMD_READ_STATUS3, `CMD_WRITE_STATUS, `CMD_WRITE_STATUS2, `CMD_WRITE_STATUS3, `CMD_PAGE_PROGRAM, + `CMD_INDIVIDUAL_LOCK, `CMD_INDIVIDUAL_UNLOCK, `CMD_READ_BLOCK_LOCK, `CMD_GLOBAL_BLOCK_LOCK, `CMD_GLOBAL_BLOCK_UNLOCK, + `CMD_SECTOR_ERASE,`CMD_HALF_BLOCK_ERASE, `CMD_BLOCK_ERASE, `CMD_BULK_ERASE, `CMD_BULK_ERASE2, + `CMD_SUSPEND, `CMD_RESUME, `CMD_DEEP_POWERDOWN, `CMD_SET_READ_PARAM, `CMD_READ_DATA_FAST, + `CMD_READ_DATA_FAST_WRAP, `CMD_READ_DATA_FAST_QUAD_IO, `CMD_READ_SIGNATURE, `CMD_READ_ID, `CMD_READ_JEDEC_ID, + `CMD_READ_UNIQUE_ID, `CMD_DISABLE_QPI, `CMD_ENABLE_RESET, `CMD_CHIP_RESET, `CMD_READ_DATA_FAST_DTR_WRAP, + `CMD_READ_DATA_FAST_DTR, `CMD_READ_DATA_FAST_QUAD_IO_DTR : + begin + is_qpi = 1; + end + + default : + begin + is_qpi = 0; + $display("Invalid Opcode for QPI mode. (%0h)",cmd_byte); + end + endcase + end + else + is_qpi = 1; + + end +endfunction + +/****************************************************************************** + The following function returns the lockbit index based on the passed byte address + The array is indexed with BLOCK 0 sectors, then last Block sectors, + then block 1 - 510 + ******************************************************************************/ + +function integer lockbit_index; + input [23:0] byte_address; + begin + + if((byte_address[23:16] == 0) || (byte_address[23:16] == (NUM_BLOCKS-1))) + begin + if(byte_address[23:16] == 0) + lockbit_index = byte_address[15:0] / SECTORSIZE; + else + lockbit_index = (byte_address[15:0] / SECTORSIZE) + 16; + end + else + lockbit_index = byte_address[23:16] - 1 + 32; + end +endfunction + + +/****************************************************************************** + ****************************************************************************** + ****************************************************************************** + COMMAND PROTOCOL HANDLERS + + The following functions execute the command protocol for the selected function + before enabling the internal state machine to handle the function + + ****************************************************************************** + ****************************************************************************** + ******************************************************************************/ + + +/****************************************************************************** + READ/WRITE PAGE PROTOCOL HANDLERS +******************************************************************************/ + +/****************************************************************************** + The following routine will execute the Read Page command 03h, + Read Page Fast command 0bh, Read Page Fast Dual 3bh and Read Page Fast Quad 6b. + and Read Security Registers 48h and SFDP Read 5Ah +******************************************************************************/ + +task read_page; + input [1:0] fast_read; // 0 = normal, 2 = dual, 3 = quad + input [1:0] mem_read; // 0 = main array, 1 = Security Pages, 2 = SFDP + integer x; + + begin + if(!status_reg[`W25Q128JVxIM_WIP]) + begin + input_byte(byte_address[23:16]); + input_byte(byte_address[15:8]); + input_byte(byte_address[7:0]); + if(fast_read) + begin + if(flag_qpi_mode == 1) + begin + for(x = 0; x <= read_param_reg[5:4];x = x + 1) + input_byte(null_reg); + end + else + input_byte(null_reg); + end + + if(mem_read) // SFDP Read and Security Register Read, input 8 dummy bytes. + input_byte(null_reg); + + forever + begin + if(mem_read == 1) // If we're reading a security sector, process output here. + begin + case(byte_address[23:8]) + 16'h10 : + output_byte(secsi[byte_address[7:0]]); + 16'h20 : + output_byte(secsi[byte_address[7:0]+PAGESIZE]); + 16'h30 : + output_byte(secsi[byte_address[7:0]]+(2*PAGESIZE)); + default : + begin + $display("Invalid Security Page Address (%x)",byte_address); + $stop; + end + endcase + end + else if(mem_read == 2) //If we're reading SFDP, process output here. + begin + if(byte_address[23:8] == 0) + output_byte(sfdp[byte_address[7:0]]); + else + begin + $display("Invalid SFDP Page Address (%x)", byte_address); + $stop; + end + end + else // Main Array Reads + begin + byte_address = byte_address & ADDRESS_MASK; + if(fast_read == 2) + output_byte_dual(memory[byte_address]); + else if(fast_read == 3) + output_byte_quad(memory[byte_address]); + else + output_byte(memory[byte_address]); + end + + // If security register or SFDP, wrap at 256 byte boundry. Only allow to read page. + if(mem_read) + byte_address[7:0] = byte_address[7:0] + 1; + else + byte_address = byte_address + 1; + end + end + end +endtask + + +/****************************************************************************** + The following routine will execute the Read Page Fast Dual IO command bbh +******************************************************************************/ + +task read_page_dualio; + begin + + if(!status_reg[`W25Q128JVxIM_WIP]) + begin + + input_byte_dual(byte_address[23:16]); + input_byte_dual(byte_address[15:8]); + input_byte_dual(byte_address[7:0]); + input_mode_dual(mode_reg[7:2]); // Ensure that mode_reg is setup on posedge clock 22. + get_posclk_holdn; // Get dummy last clock. + + forever + begin + byte_address = byte_address & ADDRESS_MASK; + output_byte_dual(memory[byte_address]); + byte_address = byte_address + 1; + end + end + end +endtask + +/****************************************************************************** + The following routine will execute the Read Page Fast Quad IO command ebh and 0ch + For the following commands, fast_read == the command being sent. +`define CMD_READ_DATA_FAST_QUAD_IO 8'heb + +******************************************************************************/ + +task read_page_quadio; + input [7:0] cmd; + integer x; + // $display("%t read page quadio, status_reg[`W25Q128JVxIM_WIP]: %d", $time, status_reg[`W25Q128JVxIM_WIP]); + begin + + + input_byte_quad(byte_address[23:16]); + input_byte_quad(byte_address[15:8]); + input_byte_quad(byte_address[7:0]); + + + if(!status_reg[`W25Q128JVxIM_WIP]) + begin + case (cmd) + `CMD_READ_DATA_FAST_QUAD_IO : + begin + input_byte_quad(mode_reg[7:0]); + input_byte_quad(null_reg); + input_byte_quad(null_reg); + end + + `CMD_READ_DATA_FAST_WRAP : + begin + for(x = 0; x <= read_param_reg[5:4];x = x + 1) + input_byte_quad(null_reg); + end + + endcase + forever + begin + byte_address = byte_address & ADDRESS_MASK; + output_byte_quad(memory[byte_address]); + // $display("%t memory[%d]: %h", $time, byte_address, memory[byte_address]); + if(cmd == `CMD_READ_DATA_FAST_WRAP) + begin + case ({read_param_reg[1],read_param_reg[0]}) + 2'b00 : + byte_address[2:0] = byte_address[2:0] + 1; + 2'b01 : + byte_address[3:0] = byte_address[3:0] + 1; + 2'b10 : + byte_address[4:0] = byte_address[4:0] + 1; + 2'b11 : + byte_address[5:0] = byte_address[5:0] + 1; + endcase + end + else + if(!wrap_reg[4] && ( cmd == `CMD_READ_DATA_FAST_QUAD_IO )) + begin + case ({wrap_reg[6],wrap_reg[5]}) + 2'b00 : + byte_address[2:0] = byte_address[2:0] + 1; + 2'b01 : + byte_address[3:0] = byte_address[3:0] + 1; + 2'b10 : + byte_address[4:0] = byte_address[4:0] + 1; + 2'b11 : + byte_address[5:0] = byte_address[5:0] + 1; + endcase + end + else + byte_address = byte_address + 1; + + end + end + end +endtask + + +/****************************************************************************** + The following routine will execute the Write to Page command 02h. +******************************************************************************/ + +task write_page; + input quadio; + integer x; + integer address; + + begin + if(!status_reg[`W25Q128JVxIM_WIP]) + begin + input_byte(prog_byte_address[23:16]); + input_byte(prog_byte_address[15:8]); + input_byte(prog_byte_address[7:0]); + // $display("prog address: %h", prog_byte_address); + if(!write_protected(prog_byte_address)) + fill_page_latch(quadio,prog_byte_address,0); + end + end +endtask + +/****************************************************************************** + The following routine will fill the page_latch with input data in either regular or quad io. + ******************************************************************************/ + +task fill_page_latch; + input quadio; + input [23:0] prog_address; + input flag_secsi; + integer x; + integer address; + + // $display("quadio: %d prog_addr: %h flag_secsi: %d", quadio, prog_address, flag_secsi); + begin + // Move memory page into page latch + if(flag_secsi) + begin + address = (prog_address >> 4) - 23'h100; + address[7:0] = 0; + end + else + begin + address = prog_address; + address[7:0] = 0; + end + for(x = 0; x < PAGESIZE; x=x+1) + page_latch[x] = flag_secsi ? secsi[address+x] : memory[address+x]; + + // Now update page latch with input data and signal a page_program operation + forever + begin + if(quadio) + input_byte_quad(temp); + else + input_byte(temp); + page_latch[prog_address[7:0]] = temp; + // $display("temp: %h", temp); + if(flag_secsi) + flag_prog_secsi_page = 1; + else + flag_prog_page = 1; + prog_address[7:0] = prog_address[7:0] + 1; + end + end +endtask + + +//////////////////////////////////////////////////////////////////////////////// +// This routine dumps the main memory array to the MEM_FILENAME file. +// + +task dump_mem; + integer x; + integer file; + begin + + file = $fopen(`MEM_FILENAME); + $fwrite(file,"/* Contents of Memory Array starting from address 0. This is a standard Verilog readmemh format. */"); + + // Erase memory array to FFh state. + for(x = 0; x < (NUM_PAGES * PAGESIZE); x=x+1) + begin + if(x % 16) + $fwrite(file,"%h ", memory[x]); + else + $fwrite(file,"\n%h ", memory[x]); + end + $fclose(file); + + end +endtask + + + +/****************************************************************************** + ****************************************************************************** + ****************************************************************************** + COMMAND STATE MACHINES + ****************************************************************************** + ******************************************************************************/ + +/****************************************************************************** + The following routine occurs when flag_set_read_param goes high. + This starts the main process for the set read param process. +******************************************************************************/ + +always @(posedge flag_set_read_param) + +begin :set_read_param + + @(posedge CSn); + if(flag_set_read_param == 1) + begin + read_param_reg = read_param_reg_shadow; + end + flag_set_read_param = 0; +end + + + +/****************************************************************************** + The following routine occurs when flag_reset goes high. + This starts the main process for the reset process. +******************************************************************************/ + +always @(posedge flag_reset) + +begin :reset + + @(posedge CSn); + if((flag_reset == 1) && (flag_write_status_reg == 0)) // Execute command, except if within a write status register command. + begin + status_reg[`W25Q128JVxIM_WIP] = 1; + flag_reset_condition = 1; + #tRES1; + chip_reset(); + + end + flag_reset = 0; +end + + +/****************************************************************************** + POWER UP/DOWN STATE MACHINES +******************************************************************************/ + +/****************************************************************************** + The following routine occurs when flag_power_up_exec goes high. + This starts the main process for the power up process. +******************************************************************************/ + +always @(posedge flag_power_up_exec) + +begin :power_up + + @(posedge CSn); + if(flag_power_up_exec == 1) + begin + if(flag_power_up_sig_read == 1) + #tRES2; + else + #tRES1; + + flag_power_down = 0; + flag_power_up_exec = 0; + flag_power_up_sig_read = 0; + flag_suspend = 0; + end +end + +/****************************************************************************** + The following routine occurs when flag_power_down_exec goes high. + This starts the main process for the power down process. +******************************************************************************/ + +always @(posedge flag_power_down) + +begin :power_down + + @(posedge CSn); + if(flag_power_down == 1) + begin + #tDP; + end +end + +/****************************************************************************** + ERASE PAGE/BLOCK COMMAND STATE MACHINES +******************************************************************************/ + +/****************************************************************************** + The following routine occurs when flag_suspend goes high. + This starts the main erase process for the handling erase suspend. +******************************************************************************/ + +always @(posedge flag_suspend) + +begin :erase_suspend + + @(posedge CSn); // Wait for CSn to go high + if(flag_suspend == 1) + begin + status_reg[`SUS] = 1; + wait_reset(tSUS); + flag_suspend_enabled = 1'b1; + status_reg[`W25Q128JVxIM_WIP] = 0; + status_reg[`W25Q128JVxIM_WEL] = 0; + + end +end + +/****************************************************************************** + The following routine occurs when flag_resume goes high. + This starts the main erase process for the handling erase resume. +******************************************************************************/ + +always @(posedge flag_resume) + +begin :erase_resume + + @(posedge CSn); // Wait for CSn to go high + if(flag_resume == 1) + begin + status_reg[`SUS] = 0; + flag_suspend_enabled = 1'b0; + flag_suspend = 1'b0; + flag_resume = 1'b0; + status_reg[`W25Q128JVxIM_WEL] = 1; + status_reg[`W25Q128JVxIM_WIP] = 1; + + end +end + +/****************************************************************************** + The following routine occurs when flag_erase_sector goes high. + This starts the main erase process for the defined sector. +******************************************************************************/ + +always @(posedge flag_erase_sector) // When flag_erase_sector goes high, device becomes active + // and starts erasing the sector defined by byte_address +begin :erase_sector + integer x; + + @(posedge CSn); // Wait for CSn to go high + if(flag_erase_sector == 1) + begin + status_reg[`W25Q128JVxIM_WIP] = 1; + + wait_reset_suspend(tSE); + + for(x = 0; x < SECTORSIZE; x=x+1) + memory[(byte_address[23:12] * SECTORSIZE) + x] = 8'hff; + + status_reg[`W25Q128JVxIM_WIP] = 0; + status_reg[`W25Q128JVxIM_WEL] = 0; + end + flag_erase_sector = 0; +end + +/****************************************************************************** + The following routine occurs when flag_erase_secsi_sector goes high. + This starts the main erase process for the defined secsi sector. +******************************************************************************/ + +always @(posedge flag_erase_secsi_sector) // When flag_erase_secsi_sector goes high, device becomes active + // and starts erasing the sector defined by byte_address +begin :erase_secsi_sector + integer x; + + @(posedge CSn); // Wait for CSn to go high + if(flag_erase_secsi_sector == 1) + begin + status_reg[`W25Q128JVxIM_WIP] = 1; + + case(byte_address[23:8]) + 16'h10 : + begin + wait_reset_suspend(tSE); + for(x = 0; x < PAGESIZE; x=x+1) + secsi[x] = 8'hff; + end + 16'h20 : + begin + wait_reset_suspend(tSE); + for(x = 0; x < PAGESIZE; x=x+1) + secsi[x+PAGESIZE] = 8'hff; + end + 16'h30 : + begin + wait_reset_suspend(tSE); + for(x = 0; x < PAGESIZE; x=x+1) + secsi[x+(PAGESIZE * 2)] = 8'hff; + end + default : + begin + $display("Invalid Security Page Erase Address (%x)",byte_address); + $stop; + end + endcase + + status_reg[`W25Q128JVxIM_WIP] = 0; + status_reg[`W25Q128JVxIM_WEL] = 0; + end + flag_erase_secsi_sector = 0; +end + + + +/****************************************************************************** + The following routine occurs when flag_erase_block goes high. + This starts the main erase process for the defined block. +******************************************************************************/ + +always @(posedge flag_erase_block) // When flag_erase_block goes high, device becomes active + // and starts erasing the block defined by byte_address +begin :erase_block + integer x; + + @(posedge CSn); // Wait for CSn to go high + if(flag_erase_block == 1) + begin + status_reg[`W25Q128JVxIM_WIP] = 1; + + wait_reset_suspend(tBE2); + + for(x = 0; x < BLOCKSIZE; x=x+1) + memory[(byte_address[23:16] * BLOCKSIZE) + x] = 8'hff; + + status_reg[`W25Q128JVxIM_WIP] = 0; + status_reg[`W25Q128JVxIM_WEL] = 0; + end + flag_erase_block = 0; +end + +/****************************************************************************** + The following routine occurs when flag_erase_half_block goes high. + This starts the main erase process for the defined block. +******************************************************************************/ + +always @(posedge flag_erase_half_block) // When flag_erase_block goes high, device becomes active + // and starts erasing the block defined by byte_address +begin :erase_half_block + integer x; + + @(posedge CSn); // Wait for CSn to go high + if(flag_erase_half_block == 1) + begin + status_reg[`W25Q128JVxIM_WIP] = 1; + + wait_reset_suspend(tBE1); + + for(x = 0; x < HALFBLOCKSIZE; x=x+1) + memory[(byte_address[23:15] * HALFBLOCKSIZE) + x] = 8'hff; + + status_reg[`W25Q128JVxIM_WIP] = 0; + status_reg[`W25Q128JVxIM_WEL] = 0; + end + flag_erase_half_block = 0; +end + + + +/****************************************************************************** + The following routine occurs when flag_erase_bulk goes high. + This starts the main erase process for the entire chip. +******************************************************************************/ + +always @(posedge flag_erase_bulk) // When flag_erase_block goes high, device becomes active + // and starts erasing the block defined by page_address +begin :erase_bulk + integer x; + + @(posedge CSn); // Wait for CSn to go high + if(flag_erase_bulk == 1) + begin + status_reg[`W25Q128JVxIM_WIP] = 1; + + for(x = 0; x < 40; x=x+1) + wait_reset(tCE_40); + + for(x = 0; x < PAGESIZE * NUM_PAGES; x=x+1) + memory[x] = 8'hff; + + status_reg[`W25Q128JVxIM_WIP] = 0; + status_reg[`W25Q128JVxIM_WEL] = 0; + end + flag_erase_bulk = 0; + +end + +/****************************************************************************** + PROGRAMMING PAGE COMMAND STATE MACHINES +******************************************************************************/ + +/****************************************************************************** + The following routine occurs when flag_prog_page goes high. + This starts the program page process. +******************************************************************************/ + +always @(posedge flag_prog_page) // When flag_prog_page goes high, device becomes active + // and starts programming the page defined by page_address +begin :program_to_page + integer x; // Local loop variable only to be used here. + + @(posedge CSn); // Wait for CSn to go high + begin + status_reg[`W25Q128JVxIM_WIP] = 1; + + prog_byte_address[7:0] = 0; + for(x = 0; x < PAGESIZE; x=x+1) + begin + memory[prog_byte_address+x] = page_latch[x] & memory[prog_byte_address+x]; + wait_reset_suspend(tPP / PAGESIZE); + end + status_reg[`W25Q128JVxIM_WIP] = 0; + status_reg[`W25Q128JVxIM_WEL] = 0; + end + flag_prog_page = 0; +end + +/****************************************************************************** + The following routine occurs when flag_prog_secsi_page goes high. + This starts the program secsi page process. +******************************************************************************/ + +always @(posedge flag_prog_secsi_page) // When flag_prog_page goes high, device becomes active + // and starts programming the page defined by page_address +begin :program_to_secsi_page + integer x; // Local loop variable only to be used here. + + @(posedge CSn); // Wait for CSn to go high + begin + status_reg[`W25Q128JVxIM_WIP] = 1; + prog_byte_address[7:0] = 0; + + case(prog_byte_address[23:8]) + 16'h10 : + begin + for(x = 0; x < PAGESIZE; x=x+1) + begin + secsi[x] = page_latch[x] & secsi[x]; + wait_reset_suspend(tPP / PAGESIZE); + end + end + 16'h20 : + begin + for(x = 0; x < PAGESIZE; x=x+1) + begin + secsi[x+PAGESIZE] = page_latch[x] & secsi[x+PAGESIZE]; + wait_reset_suspend(tPP / PAGESIZE); + end + end + 16'h30 : + begin + for(x = 0; x < PAGESIZE; x=x+1) + begin + secsi[x+(PAGESIZE*2)] = page_latch[x] & secsi[x+(PAGESIZE*2)]; + wait_reset_suspend(tPP / PAGESIZE); + end + end + default : + begin + $display("Invalid Security Page Program Address (%x)",prog_byte_address); + $stop; + end + endcase + + status_reg[`W25Q128JVxIM_WIP] = 0; + status_reg[`W25Q128JVxIM_WEL] = 0; + end + flag_prog_secsi_page = 0; +end + + + +/****************************************************************************** + CONFIGURATION REGISTER COMMAND STATE MACHINES +******************************************************************************/ + +/****************************************************************************** + The following routine occurs when flag_write_status_reg goes high. + This starts the main write status register process. +******************************************************************************/ + +always @(posedge flag_write_status_reg) // When flag_write_status_reg goes high, device becomes active + // and starts writing the status_reg_shadow register into the +begin :write_status_reg + + @(posedge CSn); // Wait for CSn to go high + if(flag_write_status_reg == 1) + begin + status_reg[`W25Q128JVxIM_WIP] = 1; + status_reg[`QE] = status_reg_shadow[`QE]; + status_reg[`SRL] = status_reg_shadow[`SRL]; + status_reg[`SRP] = status_reg_shadow[`SRP]; + status_reg[`W25Q128JVxIM_BP0] = status_reg_shadow[`W25Q128JVxIM_BP0]; + status_reg[`W25Q128JVxIM_BP1] = status_reg_shadow[`W25Q128JVxIM_BP1]; + status_reg[`W25Q128JVxIM_BP2] = status_reg_shadow[`W25Q128JVxIM_BP2]; + status_reg[`SEC] = status_reg_shadow[`SEC]; + status_reg[`W25Q128JVxIM_TB] = status_reg_shadow[`W25Q128JVxIM_TB]; + status_reg[`CMPB] = status_reg_shadow[`CMPB]; + status_reg[`WPS] = status_reg_shadow[`WPS]; + status_reg[`DRV0] = status_reg_shadow[`DRV0]; + status_reg[`DRV1] = status_reg_shadow[`DRV1]; + status_reg[`HLD_RST] = status_reg_shadow[`HLD_RST]; + + // One time program OTP bits to 1. + status_reg[`LB1] = status_reg[`LB1] | status_reg_shadow[`LB1]; + status_reg[`LB2] = status_reg[`LB2] | status_reg_shadow[`LB2]; + status_reg[`LB3] = status_reg[`LB3] | status_reg_shadow[`LB3]; + + if(!flag_volatile_sr_write) + begin + + // Now set OTP bits + + status_reg_otp[`QE] = status_reg_shadow[`QE]; + status_reg_otp[`SRL] = status_reg_shadow[`SRL]; + status_reg_otp[`SRP] = status_reg_shadow[`SRP]; + status_reg_otp[`W25Q128JVxIM_BP0] = status_reg_shadow[`W25Q128JVxIM_BP0]; + status_reg_otp[`W25Q128JVxIM_BP1] = status_reg_shadow[`W25Q128JVxIM_BP1]; + status_reg_otp[`W25Q128JVxIM_BP2] = status_reg_shadow[`W25Q128JVxIM_BP2]; + status_reg_otp[`SEC] = status_reg_shadow[`SEC]; + status_reg_otp[`W25Q128JVxIM_TB] = status_reg_shadow[`W25Q128JVxIM_TB]; + status_reg_otp[`CMPB] = status_reg_shadow[`CMPB]; + status_reg_otp[`WPS] = status_reg_shadow[`WPS]; + status_reg_otp[`DRV0] = status_reg_shadow[`DRV0]; + status_reg_otp[`DRV1] = status_reg_shadow[`DRV1]; + status_reg_otp[`HLD_RST] = status_reg_shadow[`HLD_RST]; + + // One time program OTP bits to 1. + status_reg_otp[`LB1] = status_reg[`LB1] | status_reg_shadow[`LB1]; + status_reg_otp[`LB2] = status_reg[`LB2] | status_reg_shadow[`LB2]; + status_reg_otp[`LB3] = status_reg[`LB3] | status_reg_shadow[`LB3]; + + end + + // If QE bit got reset, make sure QPI mode is disabled. + if(status_reg[`QE] == 0) + flag_qpi_mode = 0; + + + if(status_reg[`W25Q128JVxIM_WEL]) + wait_reset(tW); + status_reg[`W25Q128JVxIM_WIP] = 0; + status_reg[`W25Q128JVxIM_WEL] = 0; + flag_volatile_sr_write = 0; + flag_write_status_reg = 0; + end +end + + + +endmodule // W25Q128JVxIM diff --git a/rtl/rtl_style_audit.json b/rtl/rtl_style_audit.json index 56af52c4..57c2169d 100644 --- a/rtl/rtl_style_audit.json +++ b/rtl/rtl_style_audit.json @@ -180,6 +180,7 @@ "apb4_ws2812.sv", "i2c_core.sv", "i2c_filter.sv", + "i2c_if.sv", "i2c_reg.sv", "i2s_clkgen.sv", "i2s_core.sv", @@ -225,6 +226,7 @@ "sdio_if.sv", "sdio_pkg.sv", "sdio_reg.sv", + "spi_if.sv", "spisd_clock.sv", "spisd_command.sv", "spisd_core.sv", diff --git a/tests/test_i2c.py b/tests/test_i2c.py index aba1918a..f69b6ae9 100644 --- a/tests/test_i2c.py +++ b/tests/test_i2c.py @@ -19,7 +19,6 @@ def test_i2c_controller_and_recovery_contract(tmp_path: Path) -> None: common = ROOT / "rtl/managed/clusterip/common/rtl" serial = ROOT / "rtl/ip/serial" - cluster_i2c = ROOT / "rtl/managed/clusterip/i2c/rtl" source_list = tmp_path / "i2c.fl" source_list.write_text( "\n".join( @@ -34,7 +33,7 @@ def test_i2c_controller_and_recovery_contract(tmp_path: Path) -> None: str(common / "utils/register.sv"), str(common / "utils/fifo.sv"), str(common / "cdc/cdc_sync.sv"), - str(cluster_i2c / "i2c_if.sv"), + str(serial / "i2c_if.sv"), str(serial / "i2c_filter.sv"), str(serial / "i2c_core.sv"), str(serial / "i2c_reg.sv"), diff --git a/tests/test_spisd.py b/tests/test_spisd.py index 607872a3..6d0ae275 100644 --- a/tests/test_spisd.py +++ b/tests/test_spisd.py @@ -14,7 +14,6 @@ COMMON_REGISTER = "rtl/managed/clusterip/common/rtl/utils/register.sv" INCDIRS = [ "rtl/managed/clusterip/common/rtl", "rtl/managed/clusterip/common/rtl/interface", - "rtl/managed/clusterip/spi/rtl", "rtl/ip/peripheral", "rtl/ip/storage", ] @@ -108,7 +107,7 @@ def test_spisd_wrapper_apb_and_training() -> None: [ "rtl/managed/clusterip/common/rtl/interface/apb4_if.sv", "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv", - "rtl/managed/clusterip/spi/rtl/spi_if.sv", + "rtl/ip/storage/spi_if.sv", "rtl/ip/storage/spisd_pkg.sv", "rtl/ip/storage/spisd_define.svh", COMMON_REGISTER,