Files
V2TMUMemTester/UnitTest/Sim/EDORam.hpp
2026-03-07 15:10:59 +01:00

137 lines
4.4 KiB
C++

#include <systemc>
#include <vector>
using namespace sc_core;
using namespace sc_dt;
SC_MODULE(EDORam) {
// ports (signals are active-low for strobes; true==inactive, false==asserted)
sc_in<bool> RAS; // row strobe (active low)
sc_in<bool> UCAS; // upper CAS (active low)
sc_in<bool> LCAS; // lower CAS (active low)
sc_in<bool> WE; // write enable (we treat WE==true as write for simplicity)
sc_in<bool> OE; // output enable (not strictly required)
sc_in< sc_uint<9> > ADDRESS; // address
sc_inout_rv<16> DATA; // bidirectional data bus (resolved)
SC_CTOR(EDORam)
: addressWidth_(9), dataWidth_(16), memory_(1u << 9, 0)
{
SC_METHOD(process);
sensitive << RAS << UCAS << LCAS << WE << OE << ADDRESS << DATA;
dont_initialize();
prevRAS_ = true;
prevUCAS_ = true;
prevLCAS_ = true;
}
private:
enum State { WAIT_RAS, WAIT_CAS, WAIT_DATA };
State state_{WAIT_RAS};
const unsigned addressWidth_;
const unsigned dataWidth_;
std::vector<uint16_t> memory_;
// previous values for edge detection (active-low strobes)
bool prevRAS_;
bool prevUCAS_;
bool prevLCAS_;
void process()
{
bool ras = RAS.read();
bool ucas = UCAS.read();
bool lcas = LCAS.read();
bool we = WE.read(); // true => write (chosen convention)
bool oe = OE.read();
unsigned addr = ADDRESS.read();
bool cas_asserted = (ucas == false && lcas == false); // CAS active when both low
bool cas_any_low = (ucas == false || lcas == false);
switch (state_) {
case WAIT_RAS:
// waiting for RAS falling edge (active low)
if (prevRAS_ == true && ras == false) {
state_ = WAIT_CAS;
}
break;
case WAIT_CAS:
// row is open, wait for CAS assertion (both low)
if (ras == true) { // RAS rose -> abort/return to idle
releaseDataBus();
state_ = WAIT_RAS;
}
else if (prevUCAS_ == true && ucas == false && prevLCAS_ == true && lcas == false) {
// CAS both fell -> enter data phase
state_ = WAIT_DATA;
performTransfer(addr, cas_asserted, we, oe);
}
break;
case WAIT_DATA:
// CAS low: data valid in EDO; continue to drive/sample while CAS low
if (cas_asserted) {
performTransfer(addr, cas_asserted, we, oe);
}
// CAS rising edge (both high) returns to WAIT_CAS (row still active)
if (prevUCAS_ == false && ucas == true && prevLCAS_ == false && lcas == true) {
releaseDataBus();
state_ = WAIT_CAS;
}
// RAS rising -> precharge, go idle
if (ras == true) {
releaseDataBus();
state_ = WAIT_RAS;
}
break;
}
prevRAS_ = ras;
prevUCAS_ = ucas;
prevLCAS_ = lcas;
}
void performTransfer(unsigned addr, bool cas_active, bool we, bool oe)
{
if (!cas_active) return; // only act when CAS is asserted (both low)
if (we) {
// WRITE: sample the data bus (testbench must drive DATA during write)
sc_lv<16> val = DATA.read();
// if testbench drives "Z" the conversion yields X; guard against that:
bool has_x = false;
for (unsigned i = 0; i < 16; ++i) {
if (val[i] == sc_dt::SC_LOGIC_X) has_x = true;
}
if (!has_x) {
uint16_t v = static_cast<uint16_t>(val.to_uint());
if (addr < memory_.size()) memory_[addr] = v;
}
} else {
// READ: drive the data bus with stored value (if OE allows)
if (!oe) {
// OE active (we treat OE==false as output enabled)
uint16_t v = (addr < memory_.size()) ? memory_[addr] : 0;
sc_lv<16> lv = sc_lv<16>( (uint64_t)v );
DATA.write(lv);
} else {
// OE inactive -> leave bus high-Z
releaseDataBus();
}
}
}
void releaseDataBus()
{
// drive high-impedance on resolved signal
sc_lv<16> z;
for (unsigned i = 0; i < 16; ++i) z[i] = sc_dt::SC_LOGIC_Z;
DATA.write(z);
}
};