diff --git a/devices/spdram.h b/devices/spdram.h new file mode 100644 index 0000000..c36d6f3 --- /dev/null +++ b/devices/spdram.h @@ -0,0 +1,141 @@ +/* +DingusPPC - The Experimental PowerPC Macintosh emulator +Copyright (C) 2018-20 divingkatae and maximum + (theweirdo) spatium + +(Contact divingkatae#1017 or powermax#2286 on Discord for more info) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/** @file Serial presence detect (SPD) RAM module emulation. + + Author: Max Poliakovski + + @description + Serial presence detect (SPD) is a standard that prescribes an automatic way + to supply various information about a memory module. + A SPD-compatible memory module contains a small EEPROM that stores useful + information like memory size, configuration, speed, manufacturer etc. + The content depends on the module type. + + This EEPROM is accessible via the I2C bus. The EEPROM I2C address will be + configured depending on the physical RAM slot the module is inserted to. + + A 168-Pin SDRAM slot includes three specialized pins SA0-SA2 for setting + an unique I2C address for each module EEPROM. The SA0-SA2 pins are hardwired + differently for each RAM slot. Example: + + Slot SA0-SA2 I2C address + A %000 0x50 + %000 = 0x50 + B %001 0x50 + %001 = 0x51 + + Further reading: https://en.wikipedia.org/wiki/Serial_presence_detect + */ + +#ifndef SPD_EEPROM_H +#define SPD_EEPROM_H + +#include +#include +#include +#include +#include "i2c.h" +#include "hwcomponent.h" + +enum RAMType : int { + SDRAM = 4 +}; + + +class SpdSdram168 : public HWComponent, public I2CDevice { +public: + SpdSdram168(uint8_t addr) { + this->dev_addr = addr; + this->pos = 0; + }; + + ~SpdSdram168() = default; + + bool supports_type(HWCompType type) { return type == HWCompType::RAM; }; + + void set_capacity(int capacity_megs) { + switch(capacity_megs) { + case 32: + this->eeprom_data[3] = 0xC; /* 12 rows */ + this->eeprom_data[4] = 0x8; /* 8 columns */ + this->eeprom_data[5] = 0x1; /* one bank */ + break; + case 64: + this->eeprom_data[3] = 0xC; /* 12 rows */ + this->eeprom_data[4] = 0x9; /* 9 columns */ + this->eeprom_data[5] = 0x1; /* one bank */ + break; + case 128: + this->eeprom_data[3] = 0xC; /* 12 rows */ + this->eeprom_data[4] = 0xA; /* 10 columns */ + this->eeprom_data[5] = 0x1; /* one bank */ + break; + case 256: + this->eeprom_data[3] = 0xC; /* 12 rows */ + this->eeprom_data[4] = 0xA; /* 10 columns */ + this->eeprom_data[5] = 0x2; /* two banks */ + break; + default: + throw std::invalid_argument(std::string("Unsupported capacity!")); + } + LOG_F(INFO, "SDRAM capacity set to %dMB, I2C addr = 0x%X", + capacity_megs, this->dev_addr); + }; + + void start_transaction() { this->pos = 0; }; + + bool send_subaddress(uint8_t sub_addr) { + this->pos = sub_addr; + LOG_F(9, "SDRAM subaddress set to 0x%X", sub_addr); + return true; + }; + + bool send_byte(uint8_t data) { + LOG_F(9, "SDRAM byte 0x%X received", data); + return true; + }; + + bool receive_byte(uint8_t *p_data) { + if (this->pos >= this->eeprom_data[0]) { + this->pos = 0; /* attempt to read past SPD data should wrap around */ + } + LOG_F(9, "SDRAM sending EEPROM byte 0x%X", this->eeprom_data[this->pos]); + *p_data = this->eeprom_data[this->pos++]; + return true; + }; + +private: + uint8_t dev_addr; /* I2C address */ + int pos; /* actual read position */ + + /* EEPROM content */ + uint8_t eeprom_data[256] = { + 128, /* number of bytes present */ + 8, /* log2(EEPROM size) */ + RAMType::SDRAM, /* memory type */ + + /* the following fields will be set up in set_capacity() */ + 0, /* number of row addresses */ + 0, /* number of column addresses */ + 0 /* number of banks */ + }; +}; + +#endif /* SPD_EEPROM_H */ diff --git a/machines/machinegossamer.cpp b/machines/machinegossamer.cpp index b6a5cdf..e8864e6 100644 --- a/machines/machinegossamer.cpp +++ b/machines/machinegossamer.cpp @@ -31,6 +31,22 @@ along with this program. If not, see . #include "devices/machineid.h" #include "devices/macio.h" #include "devices/viacuda.h" +#include "devices/spdram.h" + +static void setup_ram_slot(std::string name, int i2c_addr, int capacity_megs) +{ + if (!capacity_megs) + return; + + gMachineObj->add_component(name, new SpdSdram168(i2c_addr)); + SpdSdram168 *ram_dimm = dynamic_cast(gMachineObj->get_comp_by_name(name)); + ram_dimm->set_capacity(capacity_megs); + + /* register RAM DIMM with the I2C bus */ + I2CBus *i2c_bus = dynamic_cast(gMachineObj->get_comp_by_type(HWCompType::I2C_HOST)); + i2c_bus->register_device(i2c_addr, ram_dimm); +} + int create_gossamer() { @@ -67,6 +83,11 @@ int create_gossamer() return -1; } + /* configure RAM slots */ + setup_ram_slot("RAM_DIMM_1", 0x57, 64); /* RAM slot 1 -> 64MB by default */ + setup_ram_slot("RAM_DIMM_2", 0x56, 0); /* RAM slot 2 -> empty by default */ + setup_ram_slot("RAM_DIMM_3", 0x55, 0); /* RAM slot 3 -> empty by default */ + /* Init virtual CPU and request MPC750 CPU aka G3 */ ppc_cpu_init(grackle_obj, PPC_VER::MPC750);