From 30d608a01b7ba83e57d6da0a6b3b27e7dab79a3a Mon Sep 17 00:00:00 2001 From: MichailKaa Date: Sun, 5 Apr 2026 21:18:19 +0300 Subject: [PATCH] =?UTF-8?q?Add=20=D0=BA=D0=BE=D0=B4=20=D0=B4=D0=BB=D1=8F?= =?UTF-8?q?=20=D1=81=D0=BE=D1=84=D1=82=D0=BE=D0=B2=20=D0=BE=D1=80=D0=B8?= =?UTF-8?q?=D0=B3=D0=B8=D0=BD=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=D0=B3=D0=BE=20?= =?UTF-8?q?=D0=BA=D0=B0=D1=80=D1=82=D1=80=D0=B8=D0=B4=D0=B6=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + FW_orig/src/makefile | 14 +++ FW_orig/src/zx_cartridge.v | 65 ++++++++++ FW_orig/src/zx_cartridge_tb.v | 217 ++++++++++++++++++++++++++++++++++ FW_orig/zx_cartrige.qpf | 31 +++++ FW_orig/zx_cartrige.qsf | 89 ++++++++++++++ 6 files changed, 419 insertions(+) create mode 100644 FW_orig/src/makefile create mode 100644 FW_orig/src/zx_cartridge.v create mode 100644 FW_orig/src/zx_cartridge_tb.v create mode 100644 FW_orig/zx_cartrige.qpf create mode 100644 FW_orig/zx_cartrige.qsf diff --git a/.gitignore b/.gitignore index 1e2f4ca..f4366ca 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ /HW/src/__Previews /HW_adapter/Project Logs for zx_cartridge_adapter /HW_adapter/History +/FW_orig/output_files +/FW_orig/incremental_db +/FW_orig/db diff --git a/FW_orig/src/makefile b/FW_orig/src/makefile new file mode 100644 index 0000000..45f38da --- /dev/null +++ b/FW_orig/src/makefile @@ -0,0 +1,14 @@ +# Для тестирования модуля неодходимо чтобы тест назывался %имя_модуля%_tb + +TARGET ?= zx_cartridge +ICARUS = iverilog + +all: + rm -f $(TARGET) + $(ICARUS) -g2012 -o $(TARGET) $(TARGET).v $(TARGET)_tb.v + vvp $(TARGET) + rm -f $(TARGET) +clean: + rm -f $(TARGET) $(TARGET).vcd + +.PHONY: all clean \ No newline at end of file diff --git a/FW_orig/src/zx_cartridge.v b/FW_orig/src/zx_cartridge.v new file mode 100644 index 0000000..b520b3f --- /dev/null +++ b/FW_orig/src/zx_cartridge.v @@ -0,0 +1,65 @@ +// Файл: zx_cartridge.v +// Модуль картриджа ZX Spectrum +// 17.02.2026 Miсhael Kaa +// Шина адреса CPU A0...A12 подключается напрямую к микросхеме CR_ROM +`timescale 1ns / 1ps +module zx_cartridge #( + // значение по умолчанию для оригинального картриджа + parameter SELF_LOCK_VAL = 15 +)( + // Сброс + input reset_n, + // Управляющие сигналы CPU + input iorq_n, + input rd_n, + input wr_n, + input mreq_n, + // Часть шины адреса CPU + input A7, + input A13, + input A14, + input A15, + inout [7:0] D, + + // Блокировка ПЗУ ZX + output ZX_ROM_blk, + // Разрешение ПЗУ картриджа (активный низкий) + output CR_ROM_oe_n, + // Старшая часть адреса ПЗУ картриджа (A13...A18) + output [5:0] CR_ROM_A, + output [3:0] CR_ROM_CS +); + // Счётчик банков ПЗУ картриджа по 8 кБ + reg [5:0] CR_ROM_bank_cnt = 6'b0; + // Регистр самоблокировки – отключает всю логику и ПЗУ картриджа + reg self_lock = 1'b0; + // Сигнал переключения страницы: чтение или запись в порт 0x7F + wire rom_page_up = iorq_n | A7; + // CPU работает с адресами 0000...1FFF (нижние 8 кб ПЗУ) + wire lower_rom = ({A13, A14, A15} == 3'b000) ? 1'b1 : 1'b0; + + always @(negedge rom_page_up or negedge reset_n) begin + if(!reset_n) begin + CR_ROM_bank_cnt <= 6'b0; + self_lock <= 1'b0; + end else begin + // инкремент счётчика банков + CR_ROM_bank_cnt <= CR_ROM_bank_cnt + 1'b1; + // проверка достижения значения самоблокировки + if(CR_ROM_bank_cnt == SELF_LOCK_VAL) begin + self_lock <= 1'b1; + end + end + end + + assign CR_ROM_oe_n = ~lower_rom | rd_n | mreq_n | self_lock ; + assign ZX_ROM_blk = ~CR_ROM_oe_n; + assign CR_ROM_CS[0] = CR_ROM_oe_n; + assign CR_ROM_CS[1] = 1'b1; + assign CR_ROM_CS[2] = 1'b1; + assign CR_ROM_CS[3] = 1'b1; + + assign CR_ROM_A = CR_ROM_bank_cnt; + assign D = 8'bz; + +endmodule \ No newline at end of file diff --git a/FW_orig/src/zx_cartridge_tb.v b/FW_orig/src/zx_cartridge_tb.v new file mode 100644 index 0000000..141db60 --- /dev/null +++ b/FW_orig/src/zx_cartridge_tb.v @@ -0,0 +1,217 @@ +// Файл: zx_cartridge_tb.v +`timescale 1ns / 1ps + +module zx_cartridge_tb(); + // Управляющие сигналы + reg reset_n; + reg iorq_n; + reg rd_n; + reg mreq_n; + + // Полная адресная шина (16 бит) + reg [15:0] address; + + // Подключение отдельных бит к тестируемому модулю + wire A7 = address[7]; + wire A13 = address[13]; + wire A14 = address[14]; + wire A15 = address[15]; + + // Выходы тестируемого модуля + wire ZX_ROM_blk; + wire CR_ROM_oe_n; + wire [5:0] CR_ROM_A; + + // Тестируемый модуль с параметром SELF_LOCK_VAL = 15 + zx_cartridge #( + .SELF_LOCK_VAL(15) + ) uut ( + .reset_n(reset_n), + .iorq_n(iorq_n), + .rd_n(rd_n), + .mreq_n(mreq_n), + .A7(A7), + .A13(A13), + .A14(A14), + .A15(A15), + .ZX_ROM_blk(ZX_ROM_blk), + .CR_ROM_oe_n(CR_ROM_oe_n), + .CR_ROM_A(CR_ROM_A) + ); + + // Задачи для моделирования циклов Z80 + // Запись в порт (активируется iorq_n, для инкремента важен его спад) + task write_port(input [15:0] addr); + begin + address = addr; + #10; + iorq_n = 0; // начало цикла IN/OUT + #10; + iorq_n = 1; // завершение цикла – отрицательный фронт + #10; + end + endtask + + // Чтение из памяти с проверкой прямо во время цикла + task read_mem_check(input [15:0] addr, input exp_oe_n, input exp_blk); + begin + address = addr; + #10; + mreq_n = 0; // запрос памяти + rd_n = 0; // чтение + #10; // даём сигналам установиться + // проверяем прямо во время активного цикла + if (CR_ROM_oe_n !== exp_oe_n) begin + $display("ОШИБКА: read_mem_check по адресу %h: CR_ROM_oe_n ожидалось %b, получено %b", addr, exp_oe_n, CR_ROM_oe_n); + end + if (ZX_ROM_blk !== exp_blk) begin + $display("ОШИБКА: read_mem_check по адресу %h: ZX_ROM_blk ожидалось %b, получено %b", addr, exp_blk, ZX_ROM_blk); + end + #10; + mreq_n = 1; + rd_n = 1; + #10; + end + endtask + + // Простое чтение без проверки (для создания циклов) + task read_mem(input [15:0] addr); + begin + address = addr; + #10; + mreq_n = 0; + rd_n = 0; + #20; + mreq_n = 1; + rd_n = 1; + #10; + end + endtask + + // Проверка с выводом сообщения (для простых случаев) + task check_equal(input [31:0] expected, input [31:0] actual, input [80*8:0] msg); + if (expected !== actual) begin + $display("ОШИБКА: %s. Ожидалось %d, получено %d", msg, expected, actual); + end + endtask + + integer i; + + initial begin + $dumpfile("zx_cartridge.vcd"); + $dumpvars(0, zx_cartridge_tb); + + // Исходное состояние: сброс активен, все сигналы неактивны + reset_n = 0; + iorq_n = 1; + rd_n = 1; + mreq_n = 1; + address = 16'h0000; + #100; + reset_n = 1; + #10; + + // ------------------------------------------------------------ + // Тест 1: Инкремент происходит только при A7=0 и спаде iorq_n + // ------------------------------------------------------------ + $display("=== Тест 1: Условие инкремента (A7=0 и спад iorq_n) ==="); + check_equal(0, CR_ROM_A, "Начальное значение CR_ROM_A"); + + // Попытка с A7=1 – не должен инкрементироваться + write_port(16'h0080); // A7=1 (адрес 0x80) + #10; + check_equal(0, CR_ROM_A, "После записи в порт 0x80 (A7=1)"); + + // Корректный инкремент с A7=0 + write_port(16'h007F); // A7=0 + #10; + check_equal(1, CR_ROM_A, "После первой записи в 0x7F"); + + write_port(16'h007F); // второй раз + #10; + check_equal(2, CR_ROM_A, "После второй записи в 0x7F"); + + // ------------------------------------------------------------ + // Тест 2: Достижение SELF_LOCK_VAL (15) блокирует дальнейшие инкременты + // ------------------------------------------------------------ + $display("=== Тест 2: Самоблокировка при значении 15 ==="); + // Инкрементируем до 14 (с 3 до 14) + for (i = 3; i <= 14; i = i + 1) begin + write_port(16'h007F); + #10; + // Просто выводим текущее значение для информации + $display("После записи #%0d, CR_ROM_A = %0d", i, CR_ROM_A); + end + + // Пятнадцатая запись – должна активировать блокировку + write_port(16'h007F); + #10; + $display("После пятнадцатой записи, CR_ROM_A = %0d (самоблокировка активирована)", CR_ROM_A); + + // Ещё одна запись – после блокировки счётчик может измениться или нет, + // но картридж уже заблокирован, поэтому не проверяем + write_port(16'h007F); + #10; + $display("Запись после блокировки, CR_ROM_A = %0d (значение не важно)", CR_ROM_A); + + // ------------------------------------------------------------ + // Тест 3: При self_lock=1 CR_ROM_oe_n не активируется даже в нижней области ПЗУ + // ------------------------------------------------------------ + $display("=== Тест 3: CR_ROM_oe_n неактивен во время блокировки ==="); + read_mem_check(16'h0100, 1'b1, 1'b0); // ожидаем oe_n=1 (неактивен), blk=0 + + // ------------------------------------------------------------ + // Тест 4: Сброс обнуляет счётчик и снимает блокировку + // ------------------------------------------------------------ + $display("=== Тест 4: Сброс ==="); + reset_n = 0; + #20; + reset_n = 1; + #10; + check_equal(0, CR_ROM_A, "После сброса"); + check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n после сброса"); + + // ------------------------------------------------------------ + // Тест 5: Активация CR_ROM_oe_n при чтении нижних 8 кБ (self_lock=0) + // ------------------------------------------------------------ + $display("=== Тест 5: Активация CR_ROM_oe_n в нижней области ПЗУ (0x0000-0x1FFF) ==="); + + // Чтение внутри нижней области + read_mem_check(16'h0100, 1'b0, 1'b1); // ожидаем oe_n=0 (активен), blk=1 + read_mem_check(16'h1FFF, 1'b0, 1'b1); // граница нижней области + + // Чтение вне нижней области + read_mem_check(16'h2000, 1'b1, 1'b0); // A13=1 + read_mem_check(16'h4001, 1'b1, 1'b0); // A14=1 + + // ------------------------------------------------------------ + // Тест 6: Проверка влияния mreq_n и rd_n + // ------------------------------------------------------------ + $display("=== Тест 6: Управляющие сигналы mreq_n и rd_n ==="); + address = 16'h0100; + #10; + + // mreq_n=0, rd_n=1 – чтение не активно + mreq_n = 0; rd_n = 1; + #10; + check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n при rd_n=1"); + + // mreq_n=1, rd_n=0 – нет запроса памяти + mreq_n = 1; rd_n = 0; + #10; + check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n при mreq_n=1"); + + // Оба активны – должно включиться + mreq_n = 0; rd_n = 0; + #10; + check_equal(0, CR_ROM_oe_n, "CR_ROM_oe_n при обоих активных"); + + // Возврат в исходное + mreq_n = 1; rd_n = 1; + #10; + + $display("=== Все тесты завершены ==="); + $finish; + end + +endmodule \ No newline at end of file diff --git a/FW_orig/zx_cartrige.qpf b/FW_orig/zx_cartrige.qpf new file mode 100644 index 0000000..1b3fcff --- /dev/null +++ b/FW_orig/zx_cartrige.qpf @@ -0,0 +1,31 @@ +# -------------------------------------------------------------------------- # +# +# Copyright (C) 1991-2013 Altera Corporation +# Your use of Altera Corporation's design tools, logic functions +# and other software and tools, and its AMPP partner logic +# functions, and any output files from any of the foregoing +# (including device programming or simulation files), and any +# associated documentation or information are expressly subject +# to the terms and conditions of the Altera Program License +# Subscription Agreement, Altera MegaCore Function License +# Agreement, or other applicable license agreement, including, +# without limitation, that your use is for the sole purpose of +# programming logic devices manufactured by Altera and sold by +# Altera or its authorized distributors. Please refer to the +# applicable agreement for further details. +# +# -------------------------------------------------------------------------- # +# +# Quartus II 64-Bit +# Version 13.0.1 Build 232 06/12/2013 Service Pack 1 SJ Web Edition +# Date created = 17:08:17 February 17, 2026 +# +# -------------------------------------------------------------------------- # + +QUARTUS_VERSION = "13.0" +DATE = "17:08:17 February 17, 2026" + +# Revisions + +PROJECT_REVISION = "zx_cartrige" +PROJECT_REVISION = "d_fix" diff --git a/FW_orig/zx_cartrige.qsf b/FW_orig/zx_cartrige.qsf new file mode 100644 index 0000000..c4d0728 --- /dev/null +++ b/FW_orig/zx_cartrige.qsf @@ -0,0 +1,89 @@ +# -------------------------------------------------------------------------- # +# +# Copyright (C) 1991-2013 Altera Corporation +# Your use of Altera Corporation's design tools, logic functions +# and other software and tools, and its AMPP partner logic +# functions, and any output files from any of the foregoing +# (including device programming or simulation files), and any +# associated documentation or information are expressly subject +# to the terms and conditions of the Altera Program License +# Subscription Agreement, Altera MegaCore Function License +# Agreement, or other applicable license agreement, including, +# without limitation, that your use is for the sole purpose of +# programming logic devices manufactured by Altera and sold by +# Altera or its authorized distributors. Please refer to the +# applicable agreement for further details. +# +# -------------------------------------------------------------------------- # +# +# Quartus II 64-Bit +# Version 13.0.1 Build 232 06/12/2013 Service Pack 1 SJ Web Edition +# Date created = 14:32:59 February 06, 2026 +# +# -------------------------------------------------------------------------- # +# +# Notes: +# +# 1) The default values for assignments are stored in the file: +# d_fix_assignment_defaults.qdf +# If this file doesn't exist, see file: +# assignment_defaults.qdf +# +# 2) Altera recommends that you do not modify this file. This +# file is updated automatically by the Quartus II software +# and any changes you make may be lost or overwritten. +# +# -------------------------------------------------------------------------- # + + +# set_global_assignment -name FAMILY MAX7000S +# set_global_assignment -name DEVICE "EPM7064SLC44-10" +# set_global_assignment -name MAX7000_DEVICE_IO_STANDARD TTL + +set_global_assignment -name FAMILY MAX3000A +set_global_assignment -name DEVICE "EPM3032ALC44-10" +set_global_assignment -name MAX7000_DEVICE_IO_STANDARD "3.3-V LVTTL" + +set_global_assignment -name TOP_LEVEL_ENTITY zx_cartridge +set_global_assignment -name ORIGINAL_QUARTUS_VERSION "13.0 SP1" +set_global_assignment -name PROJECT_CREATION_TIME_DATE "14:32:59 FEBRUARY 06, 2026" +set_global_assignment -name LAST_QUARTUS_VERSION "13.0 SP1" +set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files +set_global_assignment -name DEVICE_FILTER_PACKAGE PLCC +set_global_assignment -name DEVICE_FILTER_PIN_COUNT 44 +set_global_assignment -name DEVICE_FILTER_SPEED_GRADE 10 +set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR "-1" +set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 +set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 +set_global_assignment -name VERILOG_FILE src/zx_cartridge.v +set_global_assignment -name CDF_FILE output_files/Chain1.cdf +set_location_assignment PIN_1 -to reset_n +set_location_assignment PIN_2 -to rd_n +set_location_assignment PIN_4 -to wr_n +set_location_assignment PIN_6 -to A14 +set_location_assignment PIN_8 -to A15 +set_location_assignment PIN_9 -to A13 +set_location_assignment PIN_11 -to CR_ROM_CS[0] +set_location_assignment PIN_12 -to CR_ROM_CS[1] +set_location_assignment PIN_16 -to CR_ROM_A[5] +set_location_assignment PIN_18 -to CR_ROM_A[4] +set_location_assignment PIN_19 -to CR_ROM_A[3] +set_location_assignment PIN_20 -to CR_ROM_A[1] +set_location_assignment PIN_21 -to CR_ROM_A[2] +set_location_assignment PIN_24 -to A7 +set_location_assignment PIN_25 -to CR_ROM_A[0] +set_location_assignment PIN_26 -to CR_ROM_oe_n +set_location_assignment PIN_14 -to CR_ROM_CS[2] +set_location_assignment PIN_28 -to D[0] +set_location_assignment PIN_29 -to D[1] +set_location_assignment PIN_31 -to D[7] +set_location_assignment PIN_33 -to ZX_ROM_blk +set_location_assignment PIN_34 -to D[2] +set_location_assignment PIN_37 -to D[6] +set_location_assignment PIN_39 -to D[4] +set_location_assignment PIN_40 -to D[3] +set_location_assignment PIN_41 -to D[5] +set_location_assignment PIN_43 -to mreq_n +set_location_assignment PIN_44 -to iorq_n + +set_location_assignment PIN_27 -to CR_ROM_CS[3] \ No newline at end of file