// -*- C++ -*- /***************************************************************************** The following code is derived, directly or indirectly, from the SystemC source code Copyright (c) 1996-2014 by all Contributors. All Rights reserved. The contents of this file are subject to the restrictions and limitations set forth in the SystemC Open Source License (the "License"); You may not use this file except in compliance with such restrictions and limitations. You may obtain instructions on how to receive a copy of the License at http://www.accellera.org/. Software distributed by Contributors under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. *****************************************************************************/ #include "scv.h" // hack to fake a true fifo_mutex #define fifo_mutex sc_mutex const unsigned ram_size = 256; class rw_task_if: virtual public sc_interface { public: typedef sc_uint<8> addr_t; typedef sc_uint<8> data_t; struct write_t { addr_t addr; data_t data; }; virtual data_t read(const addr_t*) = 0; virtual void write(const write_t*) = 0; }; SCV_EXTENSIONS(rw_task_if::write_t){ public: scv_extensions addr; scv_extensions data; SCV_EXTENSIONS_CTOR(rw_task_if::write_t) { SCV_FIELD(addr); SCV_FIELD(data); } }; class pipelined_bus_ports: public sc_module { public: sc_in clk; sc_inout rw; sc_inout addr_req; sc_inout addr_ack; sc_inout > bus_addr; sc_inout data_rdy; sc_inout > bus_data; SC_CTOR(pipelined_bus_ports) : clk("clk"), rw("rw"), addr_req("addr_req"), addr_ack("addr_ack"), bus_addr("bus_addr"), data_rdy("data_rdy"), bus_data( "bus_data") { } virtual void trace( sc_trace_file* tf ) const; }; void pipelined_bus_ports::trace( sc_trace_file* tf ) const { sc_trace(tf, clk, clk.name()); sc_trace(tf, rw, rw.name()); sc_trace(tf, addr_req, addr_req.name()); sc_trace(tf, addr_ack, addr_ack.name()); sc_trace(tf, bus_addr, bus_addr.name()); sc_trace(tf, data_rdy, data_rdy.name()); sc_trace(tf, bus_data, bus_data.name()); } class rw_pipelined_transactor: public rw_task_if, public pipelined_bus_ports { fifo_mutex addr_phase;fifo_mutex data_phase; scv_tr_stream pipelined_stream; scv_tr_stream addr_stream; scv_tr_stream data_stream; scv_tr_generator, sc_uint<8> > read_gen; scv_tr_generator, sc_uint<8> > write_gen; scv_tr_generator > addr_gen; scv_tr_generator<_scv_tr_generator_default_data, sc_uint<8> > rdata_gen; scv_tr_generator > wdata_gen; public: rw_pipelined_transactor(sc_module_name nm) : pipelined_bus_ports(nm) , addr_phase("addr_phase") , data_phase("data_phase") , pipelined_stream((std::string(name()) +".pipelined_stream").c_str(), "transactor") , addr_stream( (std::string(name()) +".addr_stream").c_str(), "transactor") , data_stream((std::string(name()) +".data_stream").c_str(), "transactor") , read_gen("read", pipelined_stream, "addr", "data") , write_gen("write", pipelined_stream, "addr", "data") , addr_gen("addr", addr_stream, "addr") , rdata_gen("rdata", data_stream, NULL, "data") , wdata_gen("wdata", data_stream, "data") { } virtual data_t read(const addr_t* p_addr); virtual void write(const write_t * req); }; rw_task_if::data_t rw_pipelined_transactor::read(const addr_t* addr) { addr_phase.lock(); scv_tr_handle h = read_gen.begin_transaction(*addr); h.record_attribute("data_size", sizeof(data_t)); scv_tr_handle h1 = addr_gen.begin_transaction(*addr, "addr_phase", h); wait(clk->posedge_event()); bus_addr = *addr; rw=false; addr_req = 1; wait(addr_ack->posedge_event()); wait(clk->negedge_event()); addr_req = 0; wait(addr_ack->negedge_event()); addr_gen.end_transaction(h1); addr_phase.unlock(); data_phase.lock(); scv_tr_handle h2 = rdata_gen.begin_transaction("data_phase", h); wait(data_rdy->posedge_event()); data_t data = bus_data.read(); wait(data_rdy->negedge_event()); rdata_gen.end_transaction(h2, data); read_gen.end_transaction(h, data); data_phase.unlock(); return data; } void rw_pipelined_transactor::write(const write_t * req) { addr_phase.lock(); scv_tr_handle h = write_gen.begin_transaction(req->addr); h.record_attribute("data_size", sizeof(data_t)); scv_tr_handle h1 = addr_gen.begin_transaction(req->addr, "addr_phase", h); wait(clk->posedge_event()); bus_addr = req->addr; rw=true; addr_req = 1; wait(addr_ack->posedge_event()); wait(clk->negedge_event()); addr_req = 0; wait(addr_ack->negedge_event()); addr_gen.end_transaction(h1); addr_phase.unlock(); data_phase.lock(); scv_tr_handle h2 = wdata_gen.begin_transaction(req->data, "data_phase", h); bus_data=req->data; wait(data_rdy->posedge_event()); wait(data_rdy->negedge_event()); wdata_gen.end_transaction(h2); write_gen.end_transaction(h, req->data); data_phase.unlock(); } class test: public sc_module { public: sc_port transactor; SC_HAS_PROCESS(test); test( ::sc_core::sc_module_name ){ SC_THREAD(main1); SC_THREAD(main2); } void main1(); void main2(); }; class write_constraint: virtual public scv_constraint_base { public: scv_smart_ptr write;SCV_CONSTRAINT_CTOR(write_constraint) { SCV_CONSTRAINT(write->addr() <= ram_size); SCV_CONSTRAINT(write->addr() != write->data()); } }; inline void process(scv_smart_ptr data) { } inline void test::main1() { // simple sequential tests for (int i = 0; i < 3; i++) { rw_task_if::addr_t addr = i; rw_task_if::data_t data = transactor->read(&addr); cout << "at time " << sc_time_stamp() << ": "; cout << "received data : " << data << endl; } scv_smart_ptr addr; for (int i = 0; i < 3; i++) { addr->next(); rw_task_if::data_t data = transactor->read(addr->get_instance()); cout << "data for address " << *addr << " is " << data << endl; } scv_smart_ptr write; for (int i = 0; i < 3; i++) { write->next(); transactor->write(write->get_instance()); cout << "send data : " << write->data << endl; } scv_smart_ptr data; scv_bag distribution; distribution.push(1, 40); distribution.push(2, 60); data->set_mode(distribution); for (int i = 0; i < 3; i++) { data->next(); process(data); } } inline void test::main2() { // simple sequential tests for (int i = 0; i < 3; i++) { rw_task_if::addr_t addr = i; rw_task_if::data_t data = transactor->read(&addr); cout << "at time " << sc_time_stamp() << ": "; cout << "received data : " << data << endl; } scv_smart_ptr addr; for (int i = 0; i < 3; i++) { addr->next(); rw_task_if::data_t data = transactor->read(addr->get_instance()); cout << "data for address " << *addr << " is " << data << endl; } scv_smart_ptr write; for (int i = 0; i < 3; i++) { write->next(); transactor->write(write->get_instance()); cout << "send data : " << write->data << endl; } scv_smart_ptr data; scv_bag distribution; distribution.push(1, 140); distribution.push(2, 160); data->set_mode(distribution); for (int i = 0; i < 3; i++) { data->next(); process(data); } } class design: public pipelined_bus_ports { std::list > outstandingAddresses; std::list outstandingType; sc_uint<8> memory[ram_size]; public: SC_HAS_PROCESS(design); design(sc_module_name nm) : pipelined_bus_ports(nm) { for (unsigned i = 0; i < ram_size; ++i) { memory[i] = i; } SC_THREAD(addr_phase); SC_THREAD(data_phase); } void addr_phase(); void data_phase(); }; inline void design::addr_phase() { while (1) { while (addr_req.read() != 1) { wait(addr_req->value_changed_event()); } sc_uint<8> _addr = bus_addr.read(); bool _rw = rw.read(); int cycle = rand() % 10 + 1; while (cycle-- > 0) { wait(clk->posedge_event()); } addr_ack = 1; wait(clk->posedge_event()); addr_ack = 0; outstandingAddresses.push_back(_addr); outstandingType.push_back(_rw); cout << "at time " << sc_time_stamp() << ": "; cout << "received request for memory address " << _addr << endl; } } inline void design::data_phase() { while (1) { while (outstandingAddresses.empty()) { wait(clk->posedge_event()); } int cycle = rand() % 10 + 1; while (cycle-- > 0) { wait(clk->posedge_event()); } if (outstandingType.front() == false) { cout << "reading memory address " << outstandingAddresses.front() << " with value " << memory[outstandingAddresses.front().to_ulong()] << endl; bus_data = memory[outstandingAddresses.front().to_ulong()]; data_rdy = 1; wait(clk->posedge_event()); data_rdy = 0; } else { cout << "writing memory address " << outstandingAddresses.front() << " with value " << bus_data << endl; memory[outstandingAddresses.front().to_ulong()]=bus_data; data_rdy = 1; wait(clk->posedge_event()); data_rdy = 0; } outstandingAddresses.pop_front(); outstandingType.pop_front(); } } extern void scv_tr_sqlite_init(); int sc_main(int argc, char *argv[]) { scv_startup(); #if 0 scv_tr_text_init(); const char* fileName = "my_db.txlog"; #else scv_tr_sqlite_init(); const char* fileName = "my_db"; #endif scv_tr_db db(fileName); scv_tr_db::set_default_db(&db); sc_trace_file* tf = sc_create_vcd_trace_file("my_db"); // create signals sc_clock clk("clk", 20.0, SC_NS, 0.5, 0.0, SC_NS, true); sc_signal rw; sc_signal addr_req; sc_signal addr_ack; sc_signal > bus_addr; sc_signal data_rdy; sc_signal > bus_data; // create modules/channels test t("t"); rw_pipelined_transactor tr("tr"); design duv("duv"); // connect them up t.transactor(tr); tr.clk(clk); tr.rw(rw); tr.addr_req(addr_req); tr.addr_ack(addr_ack); tr.bus_addr(bus_addr); tr.data_rdy(data_rdy); tr.bus_data(bus_data); tr.trace(tf); duv.clk(clk); duv.rw(rw); duv.addr_req(addr_req); duv.addr_ack(addr_ack); duv.bus_addr(bus_addr); duv.data_rdy(data_rdy); duv.bus_data(bus_data); duv.trace(tf); // Accellera SystemC >=2.2 got picky about multiple drivers. // Disable check for bus simulation. sc_report_handler::set_actions(SC_ID_MORE_THAN_ONE_SIGNAL_DRIVER_, SC_DO_NOTHING); // run the simulation sc_start(1.0, SC_MS); sc_close_vcd_trace_file(tf); return 0; }