137 lines
4.4 KiB
C++
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);
|
|
}
|
|
}; |