/* -*- 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 "vci_block_device_tsar.h" namespace soclib { namespace caba { #define tmpl(t) template t VciBlockDeviceTsar 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 & 0x3F)>>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_BUFFER_EXT) ) r_target_fsm = T_WRITE_BUFFER_EXT; else if( read && (cell == BLOCK_DEVICE_BUFFER_EXT) ) r_target_fsm = T_READ_BUFFER_EXT; 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.read() == M_IDLE) and p_vci_target.rspack.read() ) { r_buf_address = (uint64_t)p_vci_target.wdata.read(); r_target_fsm = T_IDLE; } break; } //////////////////////// case T_WRITE_BUFFER_EXT: { if ( (r_initiator_fsm.read() == M_IDLE) and p_vci_target.rspack.read() ) { r_buf_address = r_buf_address.read() + (((uint64_t)p_vci_target.wdata.read())<<32); r_target_fsm = T_IDLE; } break; } /////////////////// case T_WRITE_COUNT: { if ( (r_initiator_fsm.read() == M_IDLE) and p_vci_target.rspack.read() ) { r_nblocks = (uint32_t)p_vci_target.wdata.read(); r_target_fsm = T_IDLE; } break; } ///////////////// case T_WRITE_LBA: { if ( (r_initiator_fsm.read() == M_IDLE) and p_vci_target.rspack.read() ) { r_lba = (uint32_t)p_vci_target.wdata.read(); r_target_fsm = T_IDLE; } break; } //////////////// case T_WRITE_OP: { if ( p_vci_target.rspack.read() ) { if ( ((uint32_t)p_vci_target.wdata.read() == BLOCK_DEVICE_READ) and (r_initiator_fsm.read() == M_IDLE) ) { r_read = true; r_go = true; } else if ( ((uint32_t)p_vci_target.wdata.read() == BLOCK_DEVICE_WRITE) and (r_initiator_fsm.read() == M_IDLE) ) { r_read = false; r_go = true; } else { r_go = false; } r_target_fsm = T_IDLE; } break; } /////////////////// case T_WRITE_IRQEN: { if ( p_vci_target.rspack.read() ) { r_target_fsm = T_IDLE; r_irq_enable = (p_vci_target.wdata.read() != 0); } break; } /////////////////// case T_READ_BUFFER: case T_READ_BUFFER_EXT: 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_words_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 words in a burst // must be contained in a single cache line. // first burst => nwords = m_words_per_burst - offset // last burst => nwords = offset // other burst => nwords = m_words_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_words_count = 0; r_latency_count = m_latency; // compute r_burst_offset (zero when buffer aligned) r_burst_offset = (uint32_t)((r_buf_address.read()>>2) % m_words_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_words_per_block*4, SEEK_SET); if( ::read(m_fd, r_local_buffer, m_words_per_block*4) < 0 ) { r_initiator_fsm = M_READ_ERROR; } else { r_burst_count = 0; r_words_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 words and the number of flits in the burst // The number of flits can be smaller than the number of words // in case of 8 bytes flits... { uint32_t nwords; uint32_t offset = r_burst_offset.read(); if ( offset ) // buffer not aligned { if ( r_burst_count.read() == 0 ) nwords = m_words_per_burst - offset; else if ( r_burst_count.read() == m_bursts_per_block ) nwords = offset; else nwords = m_words_per_burst; } else // buffer aligned { nwords = m_words_per_burst; } r_burst_nwords = nwords; r_initiator_fsm = M_READ_CMD; break; } //////////////// case M_READ_CMD: // Send a multi-flits VCI WRITE command { if ( p_vci_initiator.cmdack.read() ) { uint32_t nwords = r_burst_nwords.read() - r_words_count.read(); if ( vci_param::B == 4 ) // one word per flit { if ( nwords <= 1 ) // last flit { r_initiator_fsm = M_READ_RSP; r_words_count = 0; } else // not the last flit { r_words_count = r_words_count.read() + 1; } // compute next word address and next local buffer index r_buf_address = r_buf_address.read() + 4; r_index = r_index.read() + 1; } else // 2 words per flit { if ( nwords <= 2 ) // last flit { r_initiator_fsm = M_READ_RSP; r_words_count = 0; } else // not the last flit { r_words_count = r_words_count.read() + 2; } // compute next word address and next local buffer index if ( nwords == 1 ) { r_buf_address = r_buf_address.read() + 4; r_index = r_index.read() + 1; } else { r_buf_address = r_buf_address.read() + 8; r_index = r_index.read() + 2; } } } 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 words in the burst { uint32_t nwords; uint32_t offset = r_burst_offset.read(); if ( offset ) // buffer not aligned { if ( r_burst_count.read() == 0 ) nwords = m_words_per_burst - offset; else if ( r_burst_count.read() == m_bursts_per_block ) nwords = offset; else nwords = m_words_per_burst; } else // buffer aligned { nwords = m_words_per_burst; } r_burst_nwords = nwords; 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-words VCI READ response { if ( p_vci_initiator.rspval.read() ) { bool aligned = (r_burst_offset.read() == 0); if ( (vci_param::B == 8) and (r_burst_nwords.read() > 1) ) { r_local_buffer[r_index.read()] = (uint32_t)p_vci_initiator.rdata.read(); r_local_buffer[r_index.read()+1] = (uint32_t)(p_vci_initiator.rdata.read()>>32); r_index = r_index.read() + 2; } else { 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_words_count = 0; r_buf_address = r_buf_address.read() + (r_burst_nwords.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_words_count = r_words_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_words_per_block*vci_param::B, SEEK_SET); if( ::write(m_fd, r_local_buffer, m_words_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_BUFFER_EXT: p_vci_target.cmdack = false; p_vci_target.rspval = true; p_vci_target.rdata = (uint32_t)(r_buf_address.read()>>32); 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 = 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 = 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 = 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 = 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 = m_words_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; p_vci_initiator.wdata = 0; p_vci_initiator.be = 0; p_vci_initiator.plen = (sc_dt::sc_uint)(r_burst_nwords.read()<<2); p_vci_initiator.eop = true; break; case M_READ_CMD: // It is actually a multi-words 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.plen = (sc_dt::sc_uint)(r_burst_nwords.read()<<2); p_vci_initiator.eop = ( r_words_count.read() == (r_burst_nwords.read() - 1) ); if ( (vci_param::B == 8) and ((r_burst_nwords.read() - r_words_count.read()) > 1) ) { p_vci_initiator.wdata = (uint64_t)r_local_buffer[r_index.read()] + ((uint64_t)r_local_buffer[r_index.read()+1])<<32; p_vci_initiator.be = 0xFF; } else { p_vci_initiator.wdata = r_local_buffer[r_index.read()]; p_vci_initiator.be = 0xF; } 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(/**/)::VciBlockDeviceTsar( 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_words_per_block(block_size/4), m_words_per_burst(burst_size/4), 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); dont_initialize(); sensitive << p_clk.pos(); SC_METHOD(genMoore); dont_initialize(); sensitive << p_clk.neg(); if( (block_size != 128) && (block_size != 256) && (block_size != 512) && (block_size != 1024) && (block_size != 2048) && (block_size != 4096) ) { std::cout << "Error in component VciBlockDeviceTsar : " << 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 != 4 ) && (burst_size != 8 ) && (burst_size != 16) && (burst_size != 32) && (burst_size != 64) ) { std::cout << "Error in component VciBlockDeviceTsar : " << name << std::endl; std::cout << "The burst size must be 4, 8, 16, 32 or 64 bytes" << std::endl; exit(1); } if ( m_segment.size() < 64 ) { std::cout << "Error in component VciBlockDeviceTsar : " << name << std::endl; std::cout << "The size of the segment cannot be smaller than 64 bytes" << std::endl; exit(1); } if ( (m_segment.baseAddress() & 0x0000003F) != 0 ) { std::cout << "Error in component VciBlockDeviceTsar : " << name << std::endl; std::cout << "The base address of the segment must be multiple of 64 bytes" << std::endl; exit(1); } if ( (vci_param::B != 4) and (vci_param::B != 8) ) { std::cout << "Error in component VciBlockDeviceTsar : " << name << std::endl; std::cout << "The VCI data fields must have 32 bits or 64 bits" << std::endl; exit(1); } m_fd = ::open(filename.c_str(), O_RDWR); if ( m_fd < 0 ) { std::cout << "Error in component VciBlockDeviceTsar : " << 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_words_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_BUFFER_EXT", "READ_BUFFER_EXT", "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()] << " buf = " << std::hex << r_buf_address.read() << " block = " << std::dec << r_block_count.read() << " burst = " << r_burst_count.read() << " word = " << r_words_count.read() <