From 0a97e4e0381a0de8a9b0467faabc2d33de5b8b46 Mon Sep 17 00:00:00 2001 From: Maxim Poliakovski Date: Tue, 26 Mar 2024 00:31:10 +0100 Subject: [PATCH] hmc: implement extended memory for PDM. --- devices/memctrl/hmc.cpp | 147 ++++++++++++++++++++++++++++++++++++++-- devices/memctrl/hmc.h | 45 ++++++++++-- 2 files changed, 182 insertions(+), 10 deletions(-) diff --git a/devices/memctrl/hmc.cpp b/devices/memctrl/hmc.cpp index c9e9c1e..c4d7f83 100644 --- a/devices/memctrl/hmc.cpp +++ b/devices/memctrl/hmc.cpp @@ -1,6 +1,6 @@ /* DingusPPC - The Experimental PowerPC Macintosh emulator -Copyright (C) 2018-21 divingkatae and maximum +Copyright (C) 2018-24 divingkatae and maximum (theweirdo) spatium (Contact divingkatae#1017 or powermax#2286 on Discord for more info) @@ -27,6 +27,7 @@ along with this program. If not, see . #include #include #include +#include HMC::HMC() : MemCtrlBase() { @@ -34,7 +35,7 @@ HMC::HMC() : MemCtrlBase() supports_types(HWCompType::MEM_CTRL | HWCompType::MMIO_DEV); - /* add memory mapped I/O region for the HMC control register */ + // add memory mapped I/O region for the HMC control register add_mmio_region(0x50F40000, 0x10000, this); this->ctrl_reg = 0ULL; @@ -46,7 +47,7 @@ uint32_t HMC::read(uint32_t rgn_start, uint32_t offset, int size) if (!offset) return !!(this->ctrl_reg & (1ULL << this->bit_pos++)); else - return 0; /* FIXME: what should be returned for invalid offsets? */ + return 0; // FIXME: what should be returned for invalid offsets? } void HMC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) @@ -58,13 +59,151 @@ void HMC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) bit = 1ULL << this->bit_pos++; this->ctrl_reg = (value & 1) ? this->ctrl_reg | bit : this->ctrl_reg & ~bit; + if (this->bit_pos >= HMC_CTRL_BITS) { + this->bit_pos = 0; + if (((this->ctrl_reg >> HMC_RAM_CFG_POS) & 3) != this->bank_config) { + this->bank_config = (this->ctrl_reg >> HMC_RAM_CFG_POS) & 3; + this->remap_ram_regions(); + } + } break; - case 8: /* writing to HMCBase + 8 resets internal bit position */ + case 8: // writing to HMCBase + 8 resets internal bit position this->bit_pos = 0; break; } } +void HMC::remap_ram_regions() { + uint32_t bank_b_addr; + + switch (this->bank_config & 3) { + case BANK_CFG_128MB: + bank_b_addr = BANK_B_START; + break; + case BANK_CFG_2MB: + bank_b_addr = this->mb_bank_size + BANK_SIZE_2MB; + break; + case BANK_CFG_8MB: + bank_b_addr = this->mb_bank_size + BANK_SIZE_8MB; + break; + case BANK_CFG_32MB: + bank_b_addr = this->mb_bank_size + BANK_SIZE_32MB; + break; + } + + if (this->bank_b_size && this->bank_b_start != bank_b_addr) { + AddressMapEntry *ref_entry = find_range(this->bank_b_start); + if (ref_entry) { + ref_entry->end = bank_b_addr + (ref_entry->end - ref_entry->start); + ref_entry->start = bank_b_addr; + + this->bank_b_start = bank_b_addr; + LOG_F(INFO, "%s: successfully relocated bank B mem region to 0x%X", + this->name.c_str(), bank_b_addr); + } else + LOG_F(ERROR, "%s: failed to relocate bank B mem region to 0x%X", + this->name.c_str(), bank_b_addr); + } +} + +int HMC::install_ram(uint32_t mb_bank_size, uint32_t bank_a_size, uint32_t bank_b_size) { + if (mb_bank_size != BANK_SIZE_4MB && mb_bank_size != BANK_SIZE_8MB) { + LOG_F(ERROR, "%s: invalid motherboard bank size %d", this->name.c_str(), + mb_bank_size); + return -1; + } + + if (!bank_a_size && bank_b_size) { + LOG_F(ERROR, "%s: bank A can't be empty while bank B is not empty", + this->name.c_str()); + return -1; + } + + if (!this->add_ram_region(BANK_MB_START, mb_bank_size)) { + LOG_F(ERROR, "%s: could not allocate motherboard RAM region!", this->name.c_str()); + return -1; + } + + this->mb_bank_start = BANK_MB_START; + this->mb_bank_size = mb_bank_size; + this->bank_a_start = -1; + this->bank_a_size = bank_a_size; + this->bank_b_start = -1; + this->bank_b_size = bank_b_size; + + if (bank_a_size) { + // create alias for RAM bank A (required for memory sizing) + if (!this->add_ram_region(BANK_A_ALIAS, bank_a_size)) { + LOG_F(ERROR, "%s: could not allocate region for bank A!", + this->name.c_str()); + return -1; + } + + this->bank_a_start = BANK_A_ALIAS; + + uint32_t offset = 0; + uint32_t size = bank_a_size; + + // make the main region for the bank A starting right after + // the motherboard bank + if (bank_a_size > BANK_SIZE_120MB) { + // For a full 128MB bank, the lower part of this region is hidden + // by the motherboard bank. Set up the partial mirror! + offset = mb_bank_size; + size = BANK_SIZE_120MB; + } + + if (!this->add_mem_mirror_partial(mb_bank_size, BANK_A_ALIAS, + offset, size)) { + LOG_F(ERROR, "%s: could not create mirror for RAM bank A!", + this->name.c_str()); + return -1; + } + + // Create additional aliases for bank A if the installed memory is + // smaller than 8 MB. That's because HWInit always searches those areas + // for the warm start signature. + if (bank_a_size < BANK_SIZE_8MB) { + for (uint32_t alias_start = BANK_B_START + bank_a_size - BANK_SIZE_8MB; + alias_start < BANK_B_START; alias_start += bank_a_size) { + if (!this->add_mem_mirror(alias_start, this->bank_a_start)) { + LOG_F(ERROR, "%s: could not create alias for RAM bank A!", + this->name.c_str()); + return -1; + } + } + } + } + + if (bank_b_size) { + if (!this->add_ram_region(BANK_B_START, bank_b_size)) { + LOG_F(ERROR, "%s: could not allocate region for bank B!", + this->name.c_str()); + return -1; + } + + this->bank_b_start = BANK_B_START; + + // Create additional aliases for bank B if the installed memory is + // smaller than 8 MB. That's because HWInit always searches those areas + // for the warm start signature. + if (bank_b_size < BANK_SIZE_8MB) { + for (uint32_t alias_start = BANK_A_ALIAS + bank_b_size - BANK_SIZE_8MB; + alias_start < BANK_A_ALIAS; alias_start += bank_b_size) { + if (!this->add_mem_mirror(alias_start, this->bank_b_start)) { + LOG_F(ERROR, "%s: could not create alias for RAM bank B!", + this->name.c_str()); + return -1; + } + } + } + } + + this->remap_ram_regions(); + + return 0; +} + static const DeviceDescription Hmc_Descriptor = { HMC::create, {}, {} }; diff --git a/devices/memctrl/hmc.h b/devices/memctrl/hmc.h index ac528ca..3836657 100644 --- a/devices/memctrl/hmc.h +++ b/devices/memctrl/hmc.h @@ -1,6 +1,6 @@ /* DingusPPC - The Experimental PowerPC Macintosh emulator -Copyright (C) 2018-21 divingkatae and maximum +Copyright (C) 2018-24 divingkatae and maximum (theweirdo) spatium (Contact divingkatae#1017 or powermax#2286 on Discord for more info) @@ -37,9 +37,28 @@ along with this program. If not, see . #include #include -/** Bit in the HMC control register telling us where the frame bufffer - for the on-board video is located. */ -#define HMC_VBASE_BIT 33 +/* Control register bit definitions. */ +enum { + HMC_CTRL_BITS = 35, // width of the HMC control register + HMC_RAM_CFG_POS = 29, // DRAM configuration bits position + HMC_VBASE_BIT = 33, // framebuffer base address: 0 - 0x00100000, 1 - 0x0 +}; + +/* Motherboard RAM size constans. */ +enum { + BANK_CFG_128MB = 0, // 12 x 12 address maxtrix (reset config) + BANK_CFG_2MB = 1, // 9 x 9 address maxtrix + BANK_CFG_8MB = 2, // 10 x 10 address maxtrix + BANK_CFG_32MB = 3, // 11 x 11 address maxtrix + BANK_SIZE_2MB = 0x200000, + BANK_SIZE_4MB = 0x400000, + BANK_SIZE_8MB = 0x800000, + BANK_SIZE_32MB = 0x2000000, + BANK_SIZE_120MB = 0x07800000, + BANK_MB_START = 0x00000000, // starting address of the motherboard bank + BANK_B_START = 0x08000000, + BANK_A_ALIAS = 0x10000000, +}; class HMC : public MemCtrlBase, public MMIODevice { public: @@ -54,13 +73,27 @@ public: uint32_t read(uint32_t rgn_start, uint32_t offset, int size); void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size); + int install_ram(uint32_t mb_bank_size, uint32_t bank_a_size, uint32_t bank_b_size); + uint64_t get_control_reg(void) { return this->ctrl_reg; }; +protected: + void remap_ram_regions(); + private: - int bit_pos; - uint64_t ctrl_reg; + int bit_pos = 0; + uint64_t ctrl_reg = 0; + uint8_t bank_config = BANK_CFG_128MB; + + uint32_t mb_bank_start = -1; + uint32_t bank_a_start = -1; + uint32_t bank_b_start = -1; + + uint32_t mb_bank_size = 0; + uint32_t bank_a_size = 0; + uint32_t bank_b_size = 0; }; #endif // HMC_H