/* -*- 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, SoC * manuel.bouyer@lip6.fr october 2013 * * Maintainers: bouyer */ #include #include #include #include "vci_spi.h" #include "vcispi.h" namespace soclib { namespace caba { #define tmpl(t) template t VciSpi 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_spi_fsm = S_IDLE; r_ss = 0; r_divider = 0xffff; r_ctrl_char_len = 0; r_ctrl_ie = false; r_ctrl_cpol = false; r_ctrl_cpha = false; r_ctrl_go_bsy = false; r_spi_clk_counter = 0xffff; r_spi_clk = 0; r_spi_done = false; r_irq = false; r_read = 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 ////////////////////////////////////////////////////////////////////////////// if (r_spi_done) r_ctrl_go_bsy = false; 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(); uint32_t wdata = p_vci_target.wdata.read(); sc_dt::sc_uint address = p_vci_target.address.read(); bool found = false; std::list::iterator seg; for ( seg = m_seglist.begin() ; seg != m_seglist.end() ; seg++ ) { if ( seg->contains(address) ) found = true; } if (not found) { if (p_vci_target.cmd.read() == vci_param::CMD_WRITE) r_target_fsm = T_ERROR_WRITE; else r_target_fsm = T_ERROR_READ; } else if (p_vci_target.cmd.read() != vci_param::CMD_READ && p_vci_target.cmd.read() != vci_param::CMD_WRITE) { r_target_fsm = T_ERROR_READ; } else { bool write = (p_vci_target.cmd.read() == vci_param::CMD_WRITE) & !r_ctrl_go_bsy; uint32_t cell = (uint32_t)((address & 0x3F)>>2); switch(cell) { case SPI_DATA_TXRX0: r_rdata = r_txrx[0] & (uint64_t)0x00000000ffffffffULL; if (write) { r_txrx[0] = (r_txrx[0] & (uint64_t)0xffffffff00000000ULL) | ((uint64_t)wdata); } r_target_fsm = (p_vci_target.cmd.read() == vci_param::CMD_WRITE) ? T_RSP_WRITE : T_RSP_READ; break; case SPI_DATA_TXRX1: r_rdata = r_txrx[0] >> 32; if (write) { r_txrx[0] = (r_txrx[0] & (uint64_t)0x00000000ffffffffULL) | ((uint64_t)wdata << 32); } r_target_fsm = (p_vci_target.cmd.read() == vci_param::CMD_WRITE) ? T_RSP_WRITE : T_RSP_READ; break; case SPI_DATA_TXRX2: r_rdata = r_txrx[1] & (uint64_t)0x00000000ffffffffULL; if (write) { r_txrx[1] = (r_txrx[1] & (uint64_t)0xffffffff00000000ULL) | ((uint64_t)wdata); } r_target_fsm = (p_vci_target.cmd.read() == vci_param::CMD_WRITE) ? T_RSP_WRITE : T_RSP_READ; break; case SPI_DATA_TXRX3: r_rdata = r_txrx[1] >> 32; if (write) { r_txrx[1] = (r_txrx[1] & (uint64_t)0x00000000ffffffffULL) | ((uint64_t)wdata << 32); } r_target_fsm = (p_vci_target.cmd.read() == vci_param::CMD_WRITE) ? T_RSP_WRITE : T_RSP_READ; break; case SPI_CTRL: { uint32_t data = 0; if (r_ctrl_cpol.read()) data |= SPI_CTRL_CPOL; if (r_ctrl_cpha.read()) data |= SPI_CTRL_CPHA; if (r_ctrl_ie.read()) data |= SPI_CTRL_IE_EN; if (r_ctrl_go_bsy.read()) data |= SPI_CTRL_GO_BSY; data |= (uint32_t)r_ctrl_char_len.read(); r_rdata = data; if (write) { r_ctrl_cpol = ((wdata & SPI_CTRL_CPOL) != 0); r_ctrl_cpha = ((wdata & SPI_CTRL_CPHA) != 0); r_ctrl_ie = ((wdata & SPI_CTRL_IE_EN) != 0); if (wdata & SPI_CTRL_GO_BSY) r_ctrl_go_bsy = true; r_ctrl_char_len = (wdata & SPI_CTRL_CHAR_LEN_MASK); #ifdef SOCLIB_MODULE_DEBUG if ((wdata & SPI_CTRL_GO_BSY) != 0) { std::cout << name() << " start xfer " << std::dec << (int)r_ctrl_char_len.read() << " data " << std::hex << r_txrx[1] << " " << r_txrx[0] << std::endl; } #endif } else { r_irq = r_irq & r_ctrl_go_bsy; } r_target_fsm = (p_vci_target.cmd.read() == vci_param::CMD_WRITE) ? T_RSP_WRITE : T_RSP_READ; break; } case SPI_DIVIDER: r_rdata = r_divider.read(); if (write) { #ifdef SOCLIB_MODULE_DEBUG std::cout << name() << " divider set to " << std::dec << wdata << std::endl; #endif r_divider = wdata; } r_target_fsm = (p_vci_target.cmd.read() == vci_param::CMD_WRITE) ? T_RSP_WRITE : T_RSP_READ; break; case SPI_SS: r_rdata = r_ss.read(); if (write) { r_ss = wdata; } r_target_fsm = (p_vci_target.cmd.read() == vci_param::CMD_WRITE) ? T_RSP_WRITE : T_RSP_READ; break; default: r_target_fsm = (p_vci_target.cmd.read() == vci_param::CMD_WRITE) ? T_ERROR_WRITE : T_ERROR_READ; break; } } } break; } //////////////////// case T_RSP_READ: case T_RSP_WRITE: case T_ERROR_READ: case T_ERROR_WRITE: if (p_vci_target.rspack.read() ) { r_target_fsm = T_IDLE; } break; } // end switch target fsm ////////////////////////////////////////////////////////////////////////////// // the SPI FSM controls SPI signals ////////////////////////////////////////////////////////////////////////////// if (r_ctrl_go_bsy == false) r_spi_done = false; switch (r_spi_fsm) { case S_IDLE: r_spi_clk_counter = r_divider.read(); r_spi_clk = 0; r_spi_clk_previous = r_ctrl_cpha; r_spi_clk_ignore = r_ctrl_cpha; r_spi_bit_count = r_ctrl_char_len; r_spi_out = (r_txrx[(r_ctrl_char_len -1)/ 64] >> ((r_ctrl_char_len - 1) % 64)) & (uint64_t)0x0000000000000001ULL; if (r_ctrl_go_bsy.read() && !r_spi_done.read()) r_spi_fsm = S_XMIT; break; case S_XMIT: { bool s_clk_sample; // on clock transition, sample input line, and shift data s_clk_sample = r_spi_clk ^ r_ctrl_cpha; if (!r_spi_clk_ignore) { if (r_spi_clk_previous == 0 && s_clk_sample == 1) { // low to high transition: shift and sample r_txrx[1] = (r_txrx[1] << 1) | (r_txrx[0] >> 63); r_txrx[0] = (r_txrx[0] << 1) | p_spi_miso; r_spi_bit_count = r_spi_bit_count - 1; } else if (r_spi_clk_previous == 1 && s_clk_sample == 0) { // high to low transition: change output, or stop if (r_spi_bit_count == 0) { r_spi_fsm = S_IDLE; r_irq = r_ctrl_ie; r_spi_done = true; #ifdef SOCLIB_MODULE_DEBUG0 std::cout << name() << " end xfer " << std::dec << (int)r_ctrl_char_len.read() << " data " << std::hex << r_txrx[1] << " " << r_txrx[0] << std::endl; #endif } else { r_spi_out = (r_txrx[(r_ctrl_char_len -1)/ 64] >> ((r_ctrl_char_len - 1) % 64)) & (uint64_t)0x0000000000000001ULL; } } } r_spi_clk_previous = s_clk_sample; // generate the SPI clock if (r_spi_clk_counter.read() == 0) { r_spi_clk_counter = r_divider.read(); r_spi_clk = !r_spi_clk.read(); r_spi_clk_ignore = false; } else { r_spi_clk_counter = r_spi_clk_counter.read() - 1; } break; } } ////////////////////////////////////////////////////////////////////////////// // 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 ( false ) // XXX { r_index = 0; r_block_count = 0; r_burst_count = 0; r_words_count = 0; // 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 { r_burst_count = 0; r_words_count = 0; r_initiator_fsm = M_READ_BURST; 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; #ifdef SOCLIB_MODULE_DEBUG std::cout << "vci_bd M_READ_ERROR" << std::endl; #endif } 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; #ifdef SOCLIB_MODULE_DEBUG std::cout << "vci_bd M_READ_SUCCESS" << std::endl; #endif } 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; #ifdef SOCLIB_MODULE_DEBUG std::cout << "vci_bd M_WRITE_ERROR" << std::endl; #endif } 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_block_count.read() == r_nblocks.read() - 1 ) { r_initiator_fsm = M_WRITE_SUCCESS; #ifdef SOCLIB_MODULE_DEBUG std::cout << "vci_bd M_WRITE_SUCCESS" << std::endl; #endif } else { r_burst_count = 0; r_index = 0; r_block_count = r_block_count.read() + 1; r_initiator_fsm = M_WRITE_BURST; } break; } ///////////////////// case M_WRITE_SUCCESS: case M_WRITE_ERROR: { 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; p_vci_target.rdata = 0; break; case T_RSP_READ: p_vci_target.cmdack = false; p_vci_target.rspval = true; p_vci_target.rdata = r_rdata; p_vci_target.rerror = VCI_READ_OK; break; case T_RSP_WRITE: p_vci_target.cmdack = false; p_vci_target.rspval = true; p_vci_target.rdata = 0; p_vci_target.rerror = VCI_WRITE_OK; break; case T_ERROR_READ: 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_ERROR_WRITE: p_vci_target.cmdack = false; p_vci_target.rspval = true; p_vci_target.rdata = 0; p_vci_target.rerror = VCI_WRITE_ERROR; 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); 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; p_vci_initiator.eop = ( (r_burst_nwords.read() - r_words_count.read()) <= 2 ); } else { p_vci_initiator.wdata = r_local_buffer[r_index.read()]; p_vci_initiator.be = 0xF; p_vci_initiator.eop = ( r_words_count.read() == (r_burst_nwords.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; } // SPI signals p_spi_ss = ((r_ss & 0x1) == 0); switch(r_spi_fsm) { case S_IDLE: p_spi_mosi = 0; p_spi_clk = 0; break; case S_XMIT: { bool s_clk_sample = r_spi_clk ^ r_ctrl_cpha; p_spi_clk = r_spi_clk ^ r_ctrl_cpol; if (s_clk_sample == 0) { // clock low: get data directly from shift register // as r_spi_out may be delayed by one clock cycle p_spi_mosi = (r_txrx[(r_ctrl_char_len -1)/ 64] >> ((r_ctrl_char_len - 1) % 64)) & (uint64_t)0x0000000000000001ULL; } else { // clock high: get data from saved value, as the shift register // may have changed p_spi_mosi = r_spi_out; } break; } } // IRQ signal p_irq = r_irq; } // end GenMoore() ////////////////////////////////////////////////////////////////////////////// tmpl(/**/)::VciSpi( sc_core::sc_module_name name, const soclib::common::MappingTable &mt, const soclib::common::IntTab &srcid, const soclib::common::IntTab &tgtid, const uint32_t burst_size) : caba::BaseModule(name), m_seglist(mt.getSegmentList(tgtid)), m_srcid(mt.indexForId(srcid)), m_words_per_block(512/4), m_words_per_burst(burst_size/4), m_bursts_per_block(512/burst_size), p_clk("p_clk"), p_resetn("p_resetn"), p_vci_initiator("p_vci_initiator"), p_vci_target("p_vci_target"), p_irq("p_irq"), p_spi_ss("p_spi_ss"), p_spi_clk("p_spi_clk"), p_spi_mosi("p_spi_mosi"), p_spi_miso("p_spi_miso") { std::cout << " - Building VciSpi " << name << std::endl; SC_METHOD(transition); dont_initialize(); sensitive << p_clk.pos(); SC_METHOD(genMoore); dont_initialize(); sensitive << p_clk.neg(); size_t nbsegs = 0; std::list::iterator seg; for ( seg = m_seglist.begin() ; seg != m_seglist.end() ; seg++ ) { nbsegs++; if ( (seg->baseAddress() & 0x0000003F) != 0 ) { std::cout << "Error in component VciSpi : " << name << "The base address of segment " << seg->name() << " must be multiple of 64 bytes" << std::endl; exit(1); } if ( seg->size() < 64 ) { std::cout << "Error in component VciSpi : " << name << "The size of segment " << seg->name() << " cannot be smaller than 64 bytes" << std::endl; exit(1); } std::cout << " => segment " << seg->name() << " / base = " << std::hex << seg->baseAddress() << " / size = " << seg->size() << std::endl; } if( nbsegs == 0 ) { std::cout << "Error in component VciSpi : " << name << " No segment allocated" << std::endl; exit(1); } if( (burst_size != 8 ) && (burst_size != 16) && (burst_size != 32) && (burst_size != 64) ) { std::cout << "Error in component VciSpi : " << name << " The burst size must be 8, 16, 32 or 64 bytes" << std::endl; exit(1); } if ( (vci_param::B != 4) and (vci_param::B != 8) ) { std::cout << "Error in component VciSpi : " << name << " The VCI data fields must have 32 bits or 64 bits" << std::endl; exit(1); } r_local_buffer = new uint32_t[m_words_per_block]; } // end constructor tmpl(/**/)::~VciSpi() { delete [] r_local_buffer; } ////////////////////////// tmpl(void)::print_trace() { const char* initiator_str[] = { "M_IDLE", "M_READ_BLOCK", "M_READ_BURST", "M_READ_CMD", "M_READ_RSP", "M_READ_SUCCESS", "M_READ_ERROR", "M_WRITE_BURST", "M_WRITE_CMD", "M_WRITE_RSP", "M_WRITE_BLOCK", "M_WRITE_SUCCESS", "M_WRITE_ERROR", }; const char* target_str[] = { "T_IDLE", "T_RSP_READ", "T_RSP_WRITE", "T_ERROR_READ", "T_ERROR_WRITE", }; const char* spi_str[] = { "S_IDLE", "S_XMIT", }; std::cout << name() << " _TGT : " << target_str[r_target_fsm.read()] << std::endl; std::cout << name() << " _SPI : " << spi_str[r_spi_fsm.read()] << " clk_counter " << r_spi_clk_counter.read() << " r_spi_bit_count " << r_spi_bit_count.read() << " r_ctrl_go_bsy " << (int)r_ctrl_go_bsy.read() << std::endl; std::cout << name() << " _SPI : " << " r_spi_clk " << r_spi_clk.read() << " cpol " << r_ctrl_cpol.read() << " cpha " << r_ctrl_cpha.read() << " r_spi_clk_ignore " << r_spi_clk_ignore.read() << " r_txrx 0x" << std::hex << r_txrx[1].read() << " " << r_txrx[0].read() << std::endl; std::cout << name() << " _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() <