/* -*- c++ -*- * * SOCLIB_LGPL_HEADER_BEGIN * * This file is part of SoCLib, GNU LGPLv2.1. * * SoCLib is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 of the License. * * SoCLib 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with SoCLib; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * SOCLIB_LGPL_HEADER_END * * Copyright (c) UPMC, Lip6, Asim * alain.greiner@lip6.fr april 2011 * * Maintainers: alain */ #include #include #include #include #include "vci_block_device_tsar_v4.h" namespace soclib { namespace caba { #define tmpl(t) template t VciBlockDeviceTsarV4 using namespace soclib::caba; using namespace soclib::common; //////////////////////// tmpl(void)::transition() { if(p_resetn.read() == false) { r_initiator_fsm = M_IDLE; r_target_fsm = T_IDLE; r_irq_enable = true; r_go = false; return; } ////////////////////////////////////////////////////////////////////////////// // The Target FSM controls the following registers: // r_target_fsm, r_irq_enable, r_nblocks, r_buf adress, r_lba, r_go, r_read ////////////////////////////////////////////////////////////////////////////// switch(r_target_fsm) { //////////// case T_IDLE: { if ( p_vci_target.cmdval.read() ) { r_srcid = p_vci_target.srcid.read(); r_trdid = p_vci_target.trdid.read(); r_pktid = p_vci_target.pktid.read(); sc_dt::sc_uint address = p_vci_target.address.read(); bool read = (p_vci_target.cmd.read() == vci_param::CMD_READ); uint32_t cell = (uint32_t)((address & 0x1F)>>2); if ( !read && !m_segment.contains(address) ) r_target_fsm = T_WRITE_ERROR; else if( read && !m_segment.contains(address) ) r_target_fsm = T_READ_ERROR; else if( !read && !p_vci_target.eop.read() ) r_target_fsm = T_WRITE_ERROR; else if( read && !p_vci_target.eop.read() ) r_target_fsm = T_READ_ERROR; else if( !read && (cell == BLOCK_DEVICE_BUFFER) ) r_target_fsm = T_WRITE_BUFFER; else if( read && (cell == BLOCK_DEVICE_BUFFER) ) r_target_fsm = T_READ_BUFFER; else if( !read && (cell == BLOCK_DEVICE_COUNT) ) r_target_fsm = T_WRITE_COUNT; else if( read && (cell == BLOCK_DEVICE_COUNT) ) r_target_fsm = T_READ_COUNT; else if( !read && (cell == BLOCK_DEVICE_LBA) ) r_target_fsm = T_WRITE_LBA; else if( read && (cell == BLOCK_DEVICE_LBA) ) r_target_fsm = T_READ_LBA; else if( !read && (cell == BLOCK_DEVICE_OP) ) r_target_fsm = T_WRITE_OP; else if( read && (cell == BLOCK_DEVICE_STATUS) ) r_target_fsm = T_READ_STATUS; else if( !read && (cell == BLOCK_DEVICE_IRQ_ENABLE) ) r_target_fsm = T_WRITE_IRQEN; else if( read && (cell == BLOCK_DEVICE_IRQ_ENABLE) ) r_target_fsm = T_READ_IRQEN; else if( read && (cell == BLOCK_DEVICE_SIZE) ) r_target_fsm = T_READ_SIZE; else if( read && (cell == BLOCK_DEVICE_BLOCK_SIZE) ) r_target_fsm = T_READ_BLOCK; } break; } //////////////////// case T_WRITE_BUFFER: { if ( r_initiator_fsm == M_IDLE ) r_buf_address = (uint32_t)p_vci_target.wdata.read(); if ( p_vci_target.rspack.read() ) r_target_fsm = T_IDLE; break; } /////////////////// case T_WRITE_COUNT: { if ( r_initiator_fsm == M_IDLE ) r_nblocks = (uint32_t)p_vci_target.wdata.read(); if ( p_vci_target.rspack.read() ) r_target_fsm = T_IDLE; break; } ///////////////// case T_WRITE_LBA: { if ( r_initiator_fsm == M_IDLE ) r_lba = (uint32_t)p_vci_target.wdata.read(); if ( p_vci_target.rspack.read() ) r_target_fsm = T_IDLE; break; } //////////////// case T_WRITE_OP: { if ( r_initiator_fsm == M_IDLE ) { if ( (uint32_t)p_vci_target.wdata.read() == BLOCK_DEVICE_READ ) { r_read = true; r_go = true; } else if ( (uint32_t)p_vci_target.wdata.read() == BLOCK_DEVICE_WRITE) { r_read = false; r_go = true; } } if ( p_vci_target.rspack.read() ) r_target_fsm = T_IDLE; break; } /////////////////// case T_WRITE_IRQEN: { r_irq_enable = (p_vci_target.wdata.read() != 0); if ( p_vci_target.rspack.read() ) r_target_fsm = T_IDLE; break; } /////////////////// case T_READ_BUFFER: case T_READ_COUNT: case T_READ_LBA: case T_READ_IRQEN: case T_READ_SIZE: case T_READ_BLOCK: case T_READ_ERROR: case T_WRITE_ERROR: { if ( p_vci_target.rspack.read() ) r_target_fsm = T_IDLE; break; } /////////////////// case T_READ_STATUS: { if ( p_vci_target.rspack.read() ) { r_target_fsm = T_IDLE; if( (r_initiator_fsm == M_READ_SUCCESS ) || (r_initiator_fsm == M_READ_ERROR ) || (r_initiator_fsm == M_WRITE_SUCCESS) || (r_initiator_fsm == M_WRITE_ERROR ) ) r_go = false; } break; } } // end switch target fsm ////////////////////////////////////////////////////////////////////////////// // The initiator FSM executes a loop, transfering one block per iteration. // Each block is split in bursts, and the number of bursts depends // on the memory buffer alignment on a burst boundary: // - If buffer aligned, all burst have the same length (m_flits_per burst) // and the number of bursts is (m_bursts_per_block). // - If buffer not aligned, the number of bursts is (m_bursts_per_block + 1) // and first and last burst are shorter, because all flits in a burst // must be contained in a single cache line. // first burst => nflits = m_flits_per_burst - offset // last burst => nflits = offset // other burst => nflits = m_flits_per_burst ////////////////////////////////////////////////////////////////////////////// switch( r_initiator_fsm.read() ) { //////////// case M_IDLE: // check buffer alignment to compute the number of bursts { if ( r_go.read() ) { r_index = 0; r_block_count = 0; r_burst_count = 0; r_flit_count = 0; r_latency_count = m_latency; // compute r_burst_offset (zero when buffer aligned) r_burst_offset = (r_buf_address.read()>>2) % m_flits_per_burst; // start tranfer if ( r_read.read() ) r_initiator_fsm = M_READ_BLOCK; else r_initiator_fsm = M_WRITE_BURST; } break; } ////////////////// case M_READ_BLOCK: // read one block from disk after waiting m_latency cycles { if ( r_latency_count.read() == 0 ) { r_latency_count = m_latency; ::lseek(m_fd, (r_lba + r_block_count)*m_flits_per_block*4, SEEK_SET); if( ::read(m_fd, r_local_buffer, m_flits_per_block*4) < 0 ) { r_initiator_fsm = M_READ_ERROR; } else { r_burst_count = 0; r_flit_count = 0; r_initiator_fsm = M_READ_BURST; } } else { r_latency_count = r_latency_count.read() - 1; } break; } ////////////////// case M_READ_BURST: // Compute the number of flits in the burst { uint32_t offset = r_burst_offset.read(); if ( offset ) // buffer not aligned { if ( r_burst_count.read() == 0 ) r_burst_nflits = m_flits_per_burst - offset; else if ( r_burst_count.read() == m_bursts_per_block ) r_burst_nflits = offset; else r_burst_nflits = m_flits_per_burst; } else // buffer aligned { r_burst_nflits = m_flits_per_burst; } r_initiator_fsm = M_READ_CMD; break; } //////////////// case M_READ_CMD: // Send a multi-flits VCI WRITE command { if ( p_vci_initiator.cmdack.read() ) { if ( r_flit_count == (r_burst_nflits.read() - 1) ) // last flit in a burst { r_initiator_fsm = M_READ_RSP; r_flit_count = 0; } else // not the last flit { r_flit_count = r_flit_count.read() + 1; } // compute next flit address and next local buffer index r_buf_address = r_buf_address.read() + 4; r_index = r_index.read() + 1; } break; } //////////////// case M_READ_RSP: // Wait a single flit VCI WRITE response { if ( p_vci_initiator.rspval.read() ) { bool aligned = (r_burst_offset.read() == 0); if ( (p_vci_initiator.rerror.read()&0x1) != 0 ) { r_initiator_fsm = M_READ_ERROR; } else if ( (not aligned and (r_burst_count.read() == m_bursts_per_block)) or (aligned and (r_burst_count.read() == (m_bursts_per_block-1))) ) { if ( r_block_count.read() == (r_nblocks.read()-1) ) // last burst of last block { r_initiator_fsm = M_READ_SUCCESS; } else // last burst not last block { r_index = 0; r_burst_count = 0; r_block_count = r_block_count.read() + 1; r_initiator_fsm = M_READ_BLOCK; } } else // not the last burst { r_burst_count = r_burst_count.read() + 1; r_initiator_fsm = M_READ_BURST; } } break; } /////////////////// case M_READ_SUCCESS: case M_READ_ERROR: { if( !r_go ) r_initiator_fsm = M_IDLE; break; } /////////////////// case M_WRITE_BURST: // Compute the number of flits in the burst { uint32_t offset = r_burst_offset.read(); if ( offset ) // buffer not aligned { if ( r_burst_count.read() == 0 ) r_burst_nflits = m_flits_per_burst - offset; else if ( r_burst_count.read() == m_bursts_per_block ) r_burst_nflits = offset; else r_burst_nflits = m_flits_per_burst; } else // buffer aligned { r_burst_nflits = m_flits_per_burst; } r_initiator_fsm = M_WRITE_CMD; break; } ///////////////// case M_WRITE_CMD: // This is actually a single flit VCI READ command { if ( p_vci_initiator.cmdack.read() ) r_initiator_fsm = M_WRITE_RSP; break; } ///////////////// case M_WRITE_RSP: // This is actually a multi-flits VCI READ response { bool aligned = (r_burst_offset.read() == 0); if ( p_vci_initiator.rspval.read() ) { r_local_buffer[r_index.read()] = (uint32_t)p_vci_initiator.rdata.read(); r_index = r_index.read() + 1; if ( p_vci_initiator.reop.read() ) // last flit of the burst { r_flit_count = 0; r_buf_address = r_buf_address.read() + (r_burst_nflits.read()<<2); if( (p_vci_initiator.rerror.read()&0x1) != 0 ) { r_initiator_fsm = M_WRITE_ERROR; } else if ( (not aligned and (r_burst_count.read() == m_bursts_per_block)) or (aligned and (r_burst_count.read() == (m_bursts_per_block-1))) ) // last burst { r_initiator_fsm = M_WRITE_BLOCK; } else // not the last burst { r_burst_count = r_burst_count.read() + 1; r_initiator_fsm = M_WRITE_BURST; } } else { r_flit_count = r_flit_count.read() + 1; } } break; } /////////////////// case M_WRITE_BLOCK: // write a block to disk after waiting m_latency cycles { if ( r_latency_count == 0 ) { r_latency_count = m_latency; ::lseek(m_fd, (r_lba + r_block_count)*m_flits_per_block*vci_param::B, SEEK_SET); if( ::write(m_fd, r_local_buffer, m_flits_per_block*vci_param::B) < 0 ) { r_initiator_fsm = M_WRITE_ERROR; } else if ( r_block_count.read() == r_nblocks.read() - 1 ) { r_initiator_fsm = M_WRITE_SUCCESS; } else { r_burst_count = 0; r_index = 0; r_block_count = r_block_count.read() + 1; r_initiator_fsm = M_WRITE_BURST; } } else { r_latency_count = r_latency_count - 1; } break; } ///////////////////// case M_WRITE_SUCCESS: case M_WRITE_ERROR: { if( !r_go ) r_initiator_fsm = M_IDLE; break; } } // end switch r_initiator_fsm } // end transition ////////////////////// tmpl(void)::genMoore() { // p_vci_target port p_vci_target.rsrcid = (sc_dt::sc_uint)r_srcid.read(); p_vci_target.rtrdid = (sc_dt::sc_uint)r_trdid.read(); p_vci_target.rpktid = (sc_dt::sc_uint)r_pktid.read(); p_vci_target.reop = true; switch(r_target_fsm) { case T_IDLE: p_vci_target.cmdack = true; p_vci_target.rspval = false; break; case T_READ_STATUS: p_vci_target.cmdack = false; p_vci_target.rspval = true; if (r_initiator_fsm == M_IDLE) p_vci_target.rdata = BLOCK_DEVICE_IDLE; else if(r_initiator_fsm == M_READ_SUCCESS) p_vci_target.rdata = BLOCK_DEVICE_READ_SUCCESS; else if(r_initiator_fsm == M_WRITE_SUCCESS) p_vci_target.rdata = BLOCK_DEVICE_WRITE_SUCCESS; else if(r_initiator_fsm == M_READ_ERROR) p_vci_target.rdata = BLOCK_DEVICE_READ_ERROR; else if(r_initiator_fsm == M_WRITE_ERROR) p_vci_target.rdata = BLOCK_DEVICE_WRITE_ERROR; else p_vci_target.rdata = BLOCK_DEVICE_BUSY; p_vci_target.rerror = VCI_READ_OK; break; case T_READ_BUFFER: p_vci_target.cmdack = false; p_vci_target.rspval = true; p_vci_target.rdata = (uint32_t)r_buf_address.read(); p_vci_target.rerror = VCI_READ_OK; break; case T_READ_COUNT: p_vci_target.cmdack = false; p_vci_target.rspval = true; p_vci_target.rdata = (uint32_t)r_nblocks.read(); p_vci_target.rerror = VCI_READ_OK; break; case T_READ_LBA: p_vci_target.cmdack = false; p_vci_target.rspval = true; p_vci_target.rdata = (uint32_t)r_lba.read(); p_vci_target.rerror = VCI_READ_OK; break; case T_READ_IRQEN: p_vci_target.cmdack = false; p_vci_target.rspval = true; p_vci_target.rdata = (uint32_t)r_irq_enable.read(); p_vci_target.rerror = VCI_READ_OK; break; case T_READ_SIZE: p_vci_target.cmdack = false; p_vci_target.rspval = true; p_vci_target.rdata = (uint32_t)m_device_size; p_vci_target.rerror = VCI_READ_OK; break; case T_READ_BLOCK: p_vci_target.cmdack = false; p_vci_target.rspval = true; p_vci_target.rdata = (uint32_t)m_flits_per_block*vci_param::B; p_vci_target.rerror = VCI_READ_OK; break; case T_READ_ERROR: p_vci_target.cmdack = false; p_vci_target.rspval = true; p_vci_target.rdata = 0; p_vci_target.rerror = VCI_READ_ERROR; break; case T_WRITE_ERROR: p_vci_target.cmdack = false; p_vci_target.rspval = true; p_vci_target.rdata = 0; p_vci_target.rerror = VCI_WRITE_ERROR; break; default: p_vci_target.cmdack = false; p_vci_target.rspval = true; p_vci_target.rdata = 0; p_vci_target.rerror = VCI_WRITE_OK; break; } // end switch target fsm // p_vci_initiator port p_vci_initiator.srcid = (sc_dt::sc_uint)m_srcid; p_vci_initiator.trdid = 0; p_vci_initiator.contig = true; p_vci_initiator.cons = false; p_vci_initiator.wrap = false; p_vci_initiator.cfixed = false; p_vci_initiator.clen = 0; switch (r_initiator_fsm) { case M_WRITE_CMD: // It is actually a single flit VCI read command p_vci_initiator.rspack = false; p_vci_initiator.cmdval = true; p_vci_initiator.address = (sc_dt::sc_uint)r_buf_address.read(); p_vci_initiator.cmd = vci_param::CMD_READ; p_vci_initiator.pktid = TYPE_READ_DATA_UNC; // or _MISS ? p_vci_initiator.wdata = 0; p_vci_initiator.be = (uint32_t)0xF; p_vci_initiator.plen = (sc_dt::sc_uint)(r_burst_nflits.read()<<2); p_vci_initiator.eop = true; break; case M_READ_CMD: // It is actually a multi-flits VCI WRITE command p_vci_initiator.rspack = false; p_vci_initiator.cmdval = true; p_vci_initiator.address = (sc_dt::sc_uint)r_buf_address.read(); p_vci_initiator.cmd = vci_param::CMD_WRITE; p_vci_initiator.pktid = TYPE_WRITE; p_vci_initiator.wdata = (uint32_t)r_local_buffer[r_index.read()]; p_vci_initiator.be = 0xF; p_vci_initiator.plen = (sc_dt::sc_uint)(r_burst_nflits.read()<<2); p_vci_initiator.eop = ( r_flit_count.read() == (r_burst_nflits.read() - 1) ); break; case M_READ_RSP: case M_WRITE_RSP: p_vci_initiator.rspack = true; p_vci_initiator.cmdval = false; break; default: p_vci_initiator.rspack = false; p_vci_initiator.cmdval = false; break; } // IRQ signal if(((r_initiator_fsm == M_READ_SUCCESS) || (r_initiator_fsm == M_WRITE_SUCCESS) || (r_initiator_fsm == M_READ_ERROR) || (r_initiator_fsm == M_WRITE_ERROR) ) && r_irq_enable) p_irq = true; else p_irq = false; } // end GenMoore() ////////////////////////////////////////////////////////////////////////////// tmpl(/**/)::VciBlockDeviceTsarV4( sc_core::sc_module_name name, const soclib::common::MappingTable &mt, const soclib::common::IntTab &srcid, const soclib::common::IntTab &tgtid, const std::string &filename, const uint32_t block_size, const uint32_t burst_size, const uint32_t latency) : caba::BaseModule(name), m_segment(mt.getSegment(tgtid)), m_srcid(mt.indexForId(srcid)), m_flits_per_block(block_size/vci_param::B), m_flits_per_burst(burst_size/vci_param::B), m_bursts_per_block(block_size/burst_size), m_latency(latency), p_clk("p_clk"), p_resetn("p_resetn"), p_vci_initiator("p_vci_initiator"), p_vci_target("p_vci_target"), p_irq("p_irq") { SC_METHOD(transition); sensitive_pos << p_clk; SC_METHOD(genMoore); sensitive_neg << p_clk; if( (block_size != 64 ) && (block_size != 128) && (block_size != 256) && (block_size != 512) && (block_size != 1024) && (block_size != 2048) && (block_size != 4096) ) { std::cout << "Error in component VciBlockDeviceTsarV4 : " << name << std::endl; std::cout << "The block size must be 128, 256, 512, 1024, 2048 or 4096 bytes" << std::endl; exit(1); } if( (burst_size != 1 ) && (burst_size != 2 ) && (burst_size != 4 ) && (burst_size != 8 ) && (burst_size != 16) && (burst_size != 32) && (burst_size != 64) ) { std::cout << "Error in component VciBlockDeviceTsarV4 : " << name << std::endl; std::cout << "The burst size must be 1, 2, 4, 8, 16, 32 or 64 bytes" << std::endl; exit(1); } if ( m_segment.size() < 32 ) { std::cout << "Error in component VciBlockDeviceTsarV4 : " << name << std::endl; std::cout << "The size of the segment cannot be smaller than 32 bytes" << std::endl; exit(1); } if ( (m_segment.baseAddress() & 0x0000001F) != 0 ) { std::cout << "Error in component VciBlockDeviceTsarV4 : " << name << std::endl; std::cout << "The base address of the segment must be multiple of 32 bytes" << std::endl; exit(1); } if ( vci_param::B != 4 ) { std::cout << "Error in component VciBlockDeviceTsarV4 : " << name << std::endl; std::cout << "The VCI data fields must have 32 bits" << std::endl; exit(1); } m_fd = ::open(filename.c_str(), O_RDWR); if ( m_fd < 0 ) { std::cout << "Error in component VciBlockDeviceTsarV4 : " << name << std::endl; std::cout << "Unable to open file " << filename << std::endl; exit(1); } m_device_size = lseek(m_fd, 0, SEEK_END) / block_size; if ( m_device_size > ((uint64_t)1<<32) ) { std::cout << "Warning: block device " << name << std::endl; std::cout << "The file " << filename << std::endl; std::cout << "has more blocks than addressable with the 32 bits PIBUS address" << std::endl; m_device_size = ((uint64_t)1<<32); } r_local_buffer = new uint32_t[m_flits_per_block]; } // end constructor ////////////////////////// tmpl(void)::print_trace() { const char* initiator_str[] = { "IDLE", "READ_BLOCK", "READ_BURST", "READ_CMD", "READ_RSP", "READ_TEST", "READ_SUCCESS", "READ_ERROR", "WRITE_BURST", "WRITE_CMD", "WRITE_RSP", "WRITE_BLOCK", "WRITE_SUCCESS", "WRITE_ERROR", }; const char* target_str[] = { "IDLE ", "WRITE_BUFFER", "READ_BUFFER ", "WRITE_COUNT ", "READ_COUNT ", "WRITE_LBA ", "READ_LBA ", "WRITE_OP ", "READ_STATUS ", "WRITE_IRQEN ", "READ_IRQEN ", "READ_SIZE ", "READ_BLOCK ", "READ_ERROR ", "WRITE_ERROR ", }; std::cout << "BDEV_TGT : " << target_str[r_target_fsm.read()] << " BDEV_INI : " << initiator_str[r_initiator_fsm.read()] << " block = " << r_block_count.read() << " burst = " << r_burst_count.read() << " flit = " << r_flit_count.read() <