mirror of
https://github.com/dingusdev/dingusppc.git
synced 2025-01-10 13:29:38 +00:00
Basic BigMac Ethernet controller emulation.
Emulates MII and some PHY configuration registers.
This commit is contained in:
parent
10b8366219
commit
742003b6f3
258
devices/ethernet/bigmac.cpp
Normal file
258
devices/ethernet/bigmac.cpp
Normal file
@ -0,0 +1,258 @@
|
||||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-23 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file BigMac Ethernet controller emulation. */
|
||||
|
||||
#include <devices/deviceregistry.h>
|
||||
#include <devices/ethernet/bigmac.h>
|
||||
#include <loguru.hpp>
|
||||
|
||||
BigMac::BigMac(uint8_t id) {
|
||||
set_name("BigMac");
|
||||
supports_types(HWCompType::MMIO_DEV | HWCompType::ETHER_MAC);
|
||||
|
||||
this->chip_id = id;
|
||||
this->phy_reset();
|
||||
this->mii_reset();
|
||||
}
|
||||
|
||||
uint16_t BigMac::read(uint16_t reg_offset) {
|
||||
switch (reg_offset) {
|
||||
case BigMacReg::CHIP_ID:
|
||||
return this->chip_id;
|
||||
case BigMacReg::MIF_CSR:
|
||||
return (this->mif_csr_old & ~MIF_CSR::Data_In) | (this->mii_in_bit << 3);
|
||||
default:
|
||||
LOG_F(WARNING, "%s: unimplemented register at 0x%X", this->name.c_str(),
|
||||
reg_offset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BigMac::write(uint16_t reg_offset, uint16_t value) {
|
||||
switch (reg_offset) {
|
||||
case BigMacReg::MIF_CSR:
|
||||
if (value & MIF_CSR::Data_Out_En) {
|
||||
// send bits one by one on each low-to-high transition of MIF_CSR::Clock
|
||||
if (((this->mif_csr_old ^ value) & MIF_CSR::Clock) && (value & MIF_CSR::Clock))
|
||||
this->mii_xmit_bit(!!(value & MIF_CSR::Data_Out));
|
||||
} else {
|
||||
if (((this->mif_csr_old ^ value) & MIF_CSR::Clock) && (value & MIF_CSR::Clock))
|
||||
this->mii_rcv_bit();
|
||||
}
|
||||
this->mif_csr_old = value;
|
||||
break;
|
||||
default:
|
||||
LOG_F(WARNING, "%s: unimplemented register at 0x%X is written with 0x%X",
|
||||
this->name.c_str(), reg_offset, value);
|
||||
}
|
||||
}
|
||||
|
||||
// ================ Media Independent Interface (MII) emulation ================
|
||||
bool BigMac::mii_rcv_value(uint16_t& var, uint8_t num_bits, uint8_t next_bit) {
|
||||
var = (var << 1) | (next_bit & 1);
|
||||
this->mii_bit_counter++;
|
||||
if (this->mii_bit_counter >= num_bits) {
|
||||
this->mii_bit_counter = 0;
|
||||
return true; // all bits have been received -> return true
|
||||
}
|
||||
return false; // more bits expected
|
||||
}
|
||||
|
||||
void BigMac::mii_rcv_bit() {
|
||||
switch(this->mii_state) {
|
||||
case MII_FRAME_SM::Preamble:
|
||||
this->mii_in_bit = 1; // required for OSX
|
||||
this->mii_reset();
|
||||
break;
|
||||
case MII_FRAME_SM::Turnaround:
|
||||
this->mii_in_bit = 0;
|
||||
this->mii_bit_counter = 16;
|
||||
this->mii_state = MII_FRAME_SM::Read_Data;
|
||||
break;
|
||||
case MII_FRAME_SM::Read_Data:
|
||||
if (this->mii_bit_counter) {
|
||||
--this->mii_bit_counter;
|
||||
this->mii_in_bit = (this->mii_data >> this->mii_bit_counter) & 1;
|
||||
if (!this->mii_bit_counter) {
|
||||
this->mii_state = MII_FRAME_SM::Preamble;
|
||||
}
|
||||
} else { // out of sync (shouldn't happen)
|
||||
this->mii_reset();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_F(ERROR, "%s: unhandled state %d in mii_rcv_bit", this->name.c_str(),
|
||||
this->mii_state);
|
||||
this->mii_reset();
|
||||
}
|
||||
}
|
||||
|
||||
void BigMac::mii_xmit_bit(const uint8_t bit_val) {
|
||||
switch(this->mii_state) {
|
||||
case MII_FRAME_SM::Preamble:
|
||||
if (bit_val) {
|
||||
this->mii_bit_counter++;
|
||||
if (this->mii_bit_counter >= 32) {
|
||||
this->mii_state = MII_FRAME_SM::Start;
|
||||
this->mii_in_bit = 1; // checked in OSX
|
||||
this->mii_bit_counter = 0;
|
||||
}
|
||||
} else { // zero bit -> out of sync
|
||||
this->mii_reset();
|
||||
}
|
||||
break;
|
||||
case MII_FRAME_SM::Start:
|
||||
if (this->mii_rcv_value(this->mii_start, 2, bit_val)) {
|
||||
LOG_F(9, "MII_Start=0x%X", this->mii_start);
|
||||
this->mii_state = MII_FRAME_SM::Opcode;
|
||||
}
|
||||
break;
|
||||
case MII_FRAME_SM::Opcode:
|
||||
if (this->mii_rcv_value(this->mii_opcode, 2, bit_val)) {
|
||||
LOG_F(9, "MII_Opcode=0x%X", this->mii_opcode);
|
||||
this->mii_state = MII_FRAME_SM::Phy_Address;
|
||||
}
|
||||
break;
|
||||
case MII_FRAME_SM::Phy_Address:
|
||||
if (this->mii_rcv_value(this->mii_phy_address, 5, bit_val)) {
|
||||
LOG_F(9, "MII_PHY_Address=0x%X", this->mii_phy_address);
|
||||
this->mii_state = MII_FRAME_SM::Reg_Address;
|
||||
}
|
||||
break;
|
||||
case MII_FRAME_SM::Reg_Address:
|
||||
if (this->mii_rcv_value(this->mii_reg_address, 5, bit_val)) {
|
||||
LOG_F(9, "MII_REG_Address=0x%X", this->mii_reg_address);
|
||||
|
||||
if (this->mii_start != 1)
|
||||
LOG_F(ERROR, "%s: unsupported frame type %d", this->name.c_str(),
|
||||
this->mii_start);
|
||||
if (this->mii_phy_address)
|
||||
LOG_F(ERROR, "%s: unsupported PHY address %d", this->name.c_str(),
|
||||
this->mii_phy_address);
|
||||
switch (this->mii_opcode) {
|
||||
case 1: // write
|
||||
this->mii_state = MII_FRAME_SM::Turnaround;
|
||||
break;
|
||||
case 2: // read
|
||||
this->mii_data = this->phy_reg_read(this->mii_reg_address);
|
||||
this->mii_state = MII_FRAME_SM::Turnaround;
|
||||
break;
|
||||
default:
|
||||
LOG_F(ERROR, "%s: invalid MII opcode %d", this->name.c_str(),
|
||||
this->mii_opcode);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MII_FRAME_SM::Turnaround:
|
||||
if (this->mii_rcv_value(this->mii_turnaround, 2, bit_val)) {
|
||||
if (this->mii_turnaround != 2)
|
||||
LOG_F(ERROR, "%s: unexpected turnaround 0x%X", this->name.c_str(),
|
||||
this->mii_turnaround);
|
||||
this->mii_state = MII_FRAME_SM::Write_Data;
|
||||
}
|
||||
break;
|
||||
case MII_FRAME_SM::Write_Data:
|
||||
if (this->mii_rcv_value(this->mii_data, 16, bit_val)) {
|
||||
LOG_F(9, "%s: MII data received = 0x%X", this->name.c_str(),
|
||||
this->mii_data);
|
||||
this->phy_reg_write(this->mii_reg_address, this->mii_data);
|
||||
this->mii_state = MII_FRAME_SM::Stop;
|
||||
}
|
||||
break;
|
||||
case MII_FRAME_SM::Stop:
|
||||
if (this->mii_rcv_value(this->mii_stop, 2, bit_val)) {
|
||||
LOG_F(9, "MII_Stop=0x%X", this->mii_stop);
|
||||
this->mii_reset();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_F(ERROR, "%s: unhandled state %d in mii_xmit_bit", this->name.c_str(),
|
||||
this->mii_state);
|
||||
this->mii_reset();
|
||||
}
|
||||
}
|
||||
|
||||
void BigMac::mii_reset() {
|
||||
mii_start = 0;
|
||||
mii_opcode = 0;
|
||||
mii_phy_address = 0;
|
||||
mii_reg_address = 0;
|
||||
mii_turnaround = 0;
|
||||
mii_data = 0;
|
||||
mii_stop = 0;
|
||||
this->mii_bit_counter = 0;
|
||||
this->mii_state = MII_FRAME_SM::Preamble;
|
||||
}
|
||||
|
||||
// ===================== Ethernet PHY interface emulation =====================
|
||||
void BigMac::phy_reset() {
|
||||
if (this->chip_id == EthernetCellId::Paddington) {
|
||||
this->phy_oui = 0x1E0400; // LXT970 aka ST10040 PHY
|
||||
this->phy_model = 0;
|
||||
this->phy_rev = 0;
|
||||
} else { // assume Heathrow with LXT907 PHY
|
||||
this->phy_oui = 0x1E0400; // LXT970 aka ST10040
|
||||
this->phy_model = 0;
|
||||
this->phy_rev = 0;
|
||||
}
|
||||
this->phy_anar = 0xA1; // tell the world we support 10BASE-T and 100BASE-TX
|
||||
}
|
||||
|
||||
uint16_t BigMac::phy_reg_read(uint8_t reg_num) {
|
||||
switch(reg_num) {
|
||||
case PHY_ID1:
|
||||
return (this->phy_oui >> 6) & 0xFFFFU;
|
||||
case PHY_ID2:
|
||||
return ((this->phy_oui << 10) | (phy_model << 4) | phy_rev) & 0xFFFFU;
|
||||
case PHY_ANAR:
|
||||
return this->phy_anar;
|
||||
default:
|
||||
LOG_F(ERROR, "Reading unimplemented PHY register %d", reg_num);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BigMac::phy_reg_write(uint8_t reg_num, uint16_t value) {
|
||||
switch(reg_num) {
|
||||
case PHY_BMCR:
|
||||
this->phy_bmcr = value;
|
||||
break;
|
||||
case PHY_ANAR:
|
||||
this->phy_anar = value;
|
||||
break;
|
||||
default:
|
||||
LOG_F(ERROR, "Writing unimplemented PHY register %d", reg_num);
|
||||
}
|
||||
}
|
||||
|
||||
static const DeviceDescription BigMac_Heathrow_Descriptor = {
|
||||
BigMac::create_for_heathrow, {}, {}
|
||||
};
|
||||
|
||||
static const DeviceDescription BigMac_Paddington_Descriptor = {
|
||||
BigMac::create_for_paddington, {}, {}
|
||||
};
|
||||
|
||||
REGISTER_DEVICE(BigMacHeathrow, BigMac_Heathrow_Descriptor);
|
||||
REGISTER_DEVICE(BigMacPaddington, BigMac_Paddington_Descriptor);
|
125
devices/ethernet/bigmac.h
Normal file
125
devices/ethernet/bigmac.h
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-23 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file BigMac Ethernet controller definitions. */
|
||||
|
||||
#ifndef BIG_MAC_H
|
||||
#define BIG_MAC_H
|
||||
|
||||
#include <devices/common/hwcomponent.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
|
||||
/* Ethernet cell IDs for various MacIO ASICs. */
|
||||
enum EthernetCellId : uint8_t {
|
||||
Heathrow = 0xB1,
|
||||
Paddington = 0xC7,
|
||||
};
|
||||
|
||||
/* BigMac HW registers. */
|
||||
enum BigMacReg : uint16_t {
|
||||
CHIP_ID = 0x170,
|
||||
MIF_CSR = 0x180,
|
||||
};
|
||||
|
||||
/* MIF_CSR bit definitions. */
|
||||
enum MIF_CSR : uint8_t {
|
||||
Clock = 1 << 0,
|
||||
Data_Out = 1 << 1,
|
||||
Data_Out_En = 1 << 2,
|
||||
Data_In = 1 << 3
|
||||
};
|
||||
|
||||
/* MII frame states. */
|
||||
enum MII_FRAME_SM {
|
||||
Preamble,
|
||||
Start,
|
||||
Opcode,
|
||||
Phy_Address,
|
||||
Reg_Address,
|
||||
Turnaround,
|
||||
Read_Data,
|
||||
Write_Data,
|
||||
Stop
|
||||
};
|
||||
|
||||
/* PHY control/status registers. */
|
||||
enum {
|
||||
PHY_BMCR = 0,
|
||||
PHY_ID1 = 2,
|
||||
PHY_ID2 = 3,
|
||||
PHY_ANAR = 4,
|
||||
};
|
||||
|
||||
class BigMac : public HWComponent {
|
||||
public:
|
||||
BigMac(uint8_t id);
|
||||
~BigMac() = default;
|
||||
|
||||
static std::unique_ptr<HWComponent> create_for_heathrow() {
|
||||
return std::unique_ptr<BigMac>(new BigMac(EthernetCellId::Heathrow));
|
||||
}
|
||||
|
||||
static std::unique_ptr<HWComponent> create_for_paddington() {
|
||||
return std::unique_ptr<BigMac>(new BigMac(EthernetCellId::Paddington));
|
||||
}
|
||||
|
||||
// BigMac register accessors
|
||||
uint16_t read(uint16_t reg_offset);
|
||||
void write(uint16_t reg_offset, uint16_t value);
|
||||
|
||||
// MII methods
|
||||
bool mii_rcv_value(uint16_t& var, uint8_t num_bits, uint8_t next_bit);
|
||||
void mii_rcv_bit();
|
||||
void mii_xmit_bit(const uint8_t bit_val);
|
||||
void mii_reset();
|
||||
|
||||
// PHY control/status methods
|
||||
void phy_reset();
|
||||
uint16_t phy_reg_read(uint8_t reg_num);
|
||||
void phy_reg_write(uint8_t reg_num, uint16_t value);
|
||||
|
||||
private:
|
||||
uint8_t chip_id; // BigMac Chip ID
|
||||
|
||||
// MII state
|
||||
uint8_t mif_csr_old = 0;
|
||||
uint8_t mii_bit_counter = 0;
|
||||
uint16_t mii_start = 0;
|
||||
uint16_t mii_stop = 0;
|
||||
uint16_t mii_opcode = 0;
|
||||
uint16_t mii_phy_address = 0;
|
||||
uint16_t mii_reg_address = 0;
|
||||
uint16_t mii_turnaround = 0;
|
||||
uint16_t mii_data = 0;
|
||||
uint8_t mii_in_bit = 1;
|
||||
MII_FRAME_SM mii_state = MII_FRAME_SM::Preamble;
|
||||
|
||||
// PHY state
|
||||
uint32_t phy_oui;
|
||||
uint8_t phy_model;
|
||||
uint8_t phy_rev;
|
||||
uint16_t phy_bmcr;
|
||||
uint16_t phy_anar;
|
||||
};
|
||||
|
||||
#endif // BIG_MAC_H
|
@ -93,6 +93,9 @@ HeathrowIC::HeathrowIC() : PCIDevice("mac-io/heathrow"), InterruptCtrl()
|
||||
this->swim3->set_dma_channel(this->floppy_dma.get());
|
||||
this->floppy_dma->register_dma_int(this, 2);
|
||||
|
||||
// connect Ethernet HW
|
||||
this->bmac = dynamic_cast<BigMac*>(gMachineObj->get_comp_by_type(HWCompType::ETHER_MAC));
|
||||
|
||||
// set EMMO pin status (active low)
|
||||
this->emmo_pin = GET_BIN_PROP("emmo") ^ 1;
|
||||
}
|
||||
@ -153,9 +156,12 @@ uint32_t HeathrowIC::read(uint32_t rgn_start, uint32_t offset, int size) {
|
||||
case 8:
|
||||
res = dma_read(offset - 0x8000, size);
|
||||
break;
|
||||
case 0x10:
|
||||
case 0x10: // SCSI
|
||||
res = this->mesh->read((offset >> 4) & 0xF);
|
||||
break;
|
||||
case 0x11: // Ethernet
|
||||
res = BYTESWAP_SIZED(this->bmac->read(offset & 0xFFFU), size);
|
||||
break;
|
||||
case 0x12: // ESCC compatible addressing
|
||||
if ((offset & 0xFF) < 16) {
|
||||
return this->escc->read(compat_to_macrisc[(offset >> 1) & 0xF]);
|
||||
@ -201,9 +207,12 @@ void HeathrowIC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int
|
||||
case 8:
|
||||
dma_write(offset - 0x8000, value, size);
|
||||
break;
|
||||
case 0x10:
|
||||
case 0x10: // SCSI
|
||||
this->mesh->write((offset >> 4) & 0xF, value);
|
||||
break;
|
||||
case 0x11: // Ethernet
|
||||
this->bmac->write(offset & 0xFFFU, BYTESWAP_SIZED(value, size));
|
||||
break;
|
||||
case 0x12: // ESCC compatible addressing
|
||||
if ((offset & 0xFF) < 16) {
|
||||
this->escc->write(compat_to_macrisc[(offset >> 1) & 0xF], value);
|
||||
@ -429,7 +438,9 @@ void HeathrowIC::clear_cpu_int()
|
||||
}
|
||||
|
||||
static const vector<string> Heathrow_Subdevices = {
|
||||
"NVRAM", "ViaCuda", "Scsi0", "Mesh", "Escc", "Swim3", "Ide0", "Ide1"};
|
||||
"NVRAM", "ViaCuda", "Scsi0", "Mesh", "Escc", "Swim3", "Ide0", "Ide1",
|
||||
"BigMacHeathrow"
|
||||
};
|
||||
|
||||
static const DeviceDescription Heathrow_Descriptor = {
|
||||
HeathrowIC::create, Heathrow_Subdevices, {}
|
||||
|
@ -60,6 +60,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include <devices/common/scsi/mesh.h>
|
||||
#include <devices/common/scsi/sc53c94.h>
|
||||
#include <devices/common/viacuda.h>
|
||||
#include <devices/ethernet/bigmac.h>
|
||||
#include <devices/ethernet/mace.h>
|
||||
#include <devices/floppy/swim3.h>
|
||||
#include <devices/memctrl/memctrlbase.h>
|
||||
@ -278,6 +279,7 @@ private:
|
||||
IdeChannel* ide_0; // Internal ATA
|
||||
IdeChannel* ide_1; // Media Bay ATA
|
||||
Swim3::Swim3Ctrl* swim3; // floppy disk controller
|
||||
BigMac* bmac; // Ethernet MAC cell
|
||||
|
||||
// DMA channels
|
||||
std::unique_ptr<DMAChannel> snd_out_dma;
|
||||
|
Loading…
x
Reference in New Issue
Block a user