diff --git a/src/common/util/ities.h b/src/common/util/ities.h index 58f7fc82e..11733971d 100644 --- a/src/common/util/ities.h +++ b/src/common/util/ities.h @@ -32,6 +32,22 @@ #include #include +#if defined(__GNUC__) +#ifndef LIKELY +#define LIKELY(x) ::__builtin_expect(!!(x), 1) +#endif +#ifndef UNLIKELY +#define UNLIKELY(x) ::__builtin_expect(!!(x), 0) +#endif +#else +#ifndef LIKELY +#define LIKELY(x) x +#endif +#ifndef UNLIKELY +#define UNLIKELY(x) x +#endif +#endif + #if __cplusplus < 201402L #define CONSTEXPR #else diff --git a/src/components/scc/memory.h b/src/components/scc/memory.h index ed14c4e56..72973bd58 100644 --- a/src/components/scc/memory.h +++ b/src/components/scc/memory.h @@ -18,6 +18,7 @@ #define _SYSC_MEMORY_H_ // Needed for the simple_target_socket +#include "util/ities.h" #include #include #ifndef SC_INCLUDE_DYNAMIC_PROCESSES @@ -244,16 +245,11 @@ int memory::handle_operation(tlm::tl trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE); return 0; } - if(byt) { - auto res = std::accumulate(byt, byt + trans.get_byte_enable_length(), 0xff, [](uint8_t a, uint8_t b) { return a | b; }); - if(trans.get_byte_enable_length() != len || res != 0xff) { - SC_REPORT_ERROR("TLM-2", "generic payload transaction with scattered byte enable not supported"); - trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE); - return 0; - } - } + auto scattered = + byt ? std::accumulate(byt, byt + trans.get_byte_enable_length(), 0xff, [](uint8_t a, uint8_t b) { return a & b; }) != 0xff : false; tlm::tlm_command cmd = trans.get_command(); SCCTRACE(SCMOD) << (cmd == tlm::TLM_READ_COMMAND ? "read" : "write") << " access to addr 0x" << std::hex << adr; + trans.set_dmi_allowed(allow_dmi.get_value()); trans.set_response_status(tlm::TLM_OK_RESPONSE); auto hm_entry = host_mem_lut.getEntry(adr); if(cmd == tlm::TLM_READ_COMMAND) { @@ -291,26 +287,45 @@ int memory::handle_operation(tlm::tl } } else if(cmd == tlm::TLM_WRITE_COMMAND) { delay += delay_spec_type::get_effective_value(clk_period, wr_resp_delay); - if(hm_entry.ptr) { + if(UNLIKELY(hm_entry.ptr)) { auto hm_start_offs = adr - hm_entry.base; auto hm_end_offs = adr + len - hm_entry.base; auto hm_ptr = hm_entry.ptr + hm_start_offs; auto transfer_length = hm_end_offs < hm_entry.size ? len : hm_end_offs - hm_start_offs; - std::copy(ptr, ptr + transfer_length, hm_ptr); + if(scattered) { + for(size_t i = 0; i < transfer_length; ++i) + if(byt[i] == 0xff) + *(hm_ptr + i) = *(ptr + i); + } else + std::copy(ptr, ptr + transfer_length, hm_ptr); } else { auto& p = mem(adr / mem.page_size); auto offs = adr & mem.page_addr_mask; - if((offs + len) > mem.page_size) { + if(UNLIKELY((offs + len) > mem.page_size)) { // we cross a page of the memory auto first_part = mem.page_size - offs; - std::copy(ptr, ptr + first_part, p.data() + offs); + if(scattered) { + for(size_t i = 0; i < first_part; ++i) + if(byt[i] == 0xff) + *(p.data() + offs + i) = *(ptr + i); + } else + std::copy(ptr, ptr + first_part, p.data() + offs); auto& p2 = mem((adr / mem.page_size) + 1); - std::copy(ptr + first_part, ptr + len, p2.data()); - } else { - std::copy(ptr, ptr + len, p.data() + offs); + if(scattered) { + for(size_t i = 0, j = first_part; j < len; ++i, ++j) + if(byt[j] == 0xff) + *(p2.data() + i) = *(ptr + j); + } else + std::copy(ptr + first_part, ptr + len, p2.data()); + } else { // we stay within a page of the memory + if(scattered) { + for(size_t i = 0; i < len; ++i) + if(byt[i] == 0xff) + *(p.data() + offs + i) = *(ptr + i); + } else + std::copy(ptr, ptr + len, p.data() + offs); } } } - trans.set_dmi_allowed(allow_dmi.get_value()); return len; } diff --git a/tests/memory_subsys/memory_test.cpp b/tests/memory_subsys/memory_test.cpp index c794895de..eb31e6cc6 100644 --- a/tests/memory_subsys/memory_test.cpp +++ b/tests/memory_subsys/memory_test.cpp @@ -67,6 +67,43 @@ TEST_CASE("dmi_access", "[memory][tlm-level]") { sc_start(dut.clk.read()); } +TEST_CASE("scattered_access", "[memory][tlm-level]") { + auto& dut = factory::get(); + + auto f = [&dut](uint64_t addr) { + sc_core::sc_time delay = sc_core::SC_ZERO_TIME; + + tlm::tlm_generic_payload init; + prepare_trans(init, tlm::TLM_WRITE_COMMAND, addr, 16); + for(size_t i = 0; i < 16; ++i) + *(init.get_data_ptr() + i) = 0; + dut.mem3.handle_operation(init, delay); + + std::array write_data{{0xAAu, 0xBBu, 0xCC, 0xDD}}; + std::array be_data{{0xFFu, 0x00u, 0xFF, 0x00}}; + tlm::tlm_generic_payload write; + prepare_trans(write, tlm::TLM_WRITE_COMMAND, addr, 4); + std::copy(write_data.begin(), write_data.end(), write.get_data_ptr()); + write.set_byte_enable_length(be_data.size()); + write.set_byte_enable_ptr(be_data.data()); + dut.mem3.handle_operation(write, delay); + + std::array read_buf{{0xEEu, 0x00u, 0x00u, 0xEEu}}; + tlm::tlm_generic_payload read; + prepare_trans(read, tlm::TLM_READ_COMMAND, addr, 4); + dut.mem3.handle_operation(read, delay); + std::copy(read.get_data_ptr(), read.get_data_ptr() + read.get_data_length(), read_buf.begin()); + + REQUIRE(read_buf[0] == write_data[0]); + REQUIRE(read_buf[1] == 0x00u); + REQUIRE(read_buf[2] == write_data[2]); + REQUIRE(read_buf[3] == 0x00u); + }; + f(0x100); + constexpr uint64_t kPageSize = dut.mem3.getPageSize(); + f(kPageSize - 2); +} + TEST_CASE("page_boundary_check", "[memory][tlm-level]") { auto& dut = factory::get(); constexpr uint64_t kPageSize = dut.mem3.getPageSize();