Add код для софтов оригинального картриджа.

This commit is contained in:
MichailKaa 2026-04-05 21:18:19 +03:00
parent 569d85e556
commit 30d608a01b
6 changed files with 419 additions and 0 deletions

3
.gitignore vendored
View File

@ -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

14
FW_orig/src/makefile Normal file
View File

@ -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

View File

@ -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

View File

@ -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

31
FW_orig/zx_cartrige.qpf Normal file
View File

@ -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"

89
FW_orig/zx_cartrige.qsf Normal file
View File

@ -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]