URL
https://opencores.org/ocsvn/s80186/s80186/trunk
Subversion Repositories s80186
[/] [s80186/] [trunk/] [tests/] [rtl/] [TestLoadStore.cpp] - Rev 2
Compare with Previous | Blame | View Log
// Copyright Jamie Iles, 2017 // // This file is part of s80x86. // // s80x86 is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // s80x86 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with s80x86. If not, see <http://www.gnu.org/licenses/>. #include <gtest/gtest.h> #include <VLoadStore.h> #include <map> #include <memory> #include "VerilogTestbench.h" using physaddr = uint32_t; class LoadStoreTestbench : public VerilogTestbench<VLoadStore>, public ::testing::Test { public: enum MemWidth { WIDTH_8, WIDTH_16 }; LoadStoreTestbench(); void add_memory(physaddr start, const std::vector<uint8_t> &bytes); std::vector<uint16_t> get_read_values() const { return read_values; } void write_mar(physaddr address) { after_n_cycles(0, [&] { this->dut.mar_in = address; this->dut.write_mar = 1; after_n_cycles(0, [&] { this->dut.write_mar = 0; this->dut.mar_in = 0; }); }); cycle(); } void write_mdr(physaddr address) { after_n_cycles(0, [&] { this->dut.mdr_in = address; this->dut.write_mdr = 1; after_n_cycles(1, [&] { this->dut.write_mdr = 0; this->dut.mdr_in = 0; }); }); cycle(); } void read(MemWidth width) { access(width, false); } void write(MemWidth width) { access(width, true); } std::map<physaddr, uint8_t> mem_bytes; private: void access(MemWidth width, bool write) { after_n_cycles(0, [&] { this->dut.is_8bit = width == WIDTH_8; this->dut.start = 1; this->dut.wr_en = write; after_n_cycles(1, [&] { this->dut.start = 0; }); }); for (auto i = 0; i < 100; ++i) { cycle(); if (dut.complete) return; } FAIL() << "failed to complete memory operation" << std::endl; } std::vector<uint16_t> read_values; bool reading; bool writing; }; LoadStoreTestbench::LoadStoreTestbench() : reading(false), writing(false) { reset(); periodic(ClockCapture, [&] { if (!this->dut.reset && this->dut.complete && !this->dut.wr_en) read_values.push_back(this->dut.mdr_out); }); periodic(ClockSetup, [&] { if (!this->dut.reset && this->dut.m_access && !reading && !this->dut.wr_en) { reading = true; if (mem_bytes.find(this->dut.m_addr << 1) == mem_bytes.end()) FAIL() << "no memory at 0x" << std::hex << this->dut.m_addr; after_n_cycles(4, [&] { this->dut.m_data_in = mem_bytes[this->dut.m_addr << 1] | (mem_bytes[(this->dut.m_addr << 1) + 1] << 8); this->dut.m_ack = 1; after_n_cycles(1, [&] { this->dut.m_ack = 0; reading = false; }); }); } }); periodic(ClockSetup, [&] { if (!this->dut.reset && this->dut.m_access && !writing && this->dut.wr_en) { writing = true; after_n_cycles(4, [&] { if (this->dut.m_bytesel & 0x1) this->mem_bytes[this->dut.m_addr << 1] = this->dut.m_data_out & 0xff; if (this->dut.m_bytesel & 0x2) this->mem_bytes[(this->dut.m_addr << 1) + 1] = (this->dut.m_data_out >> 8) & 0xff; this->dut.m_ack = 1; after_n_cycles(1, [&] { this->dut.m_ack = 0; writing = false; }); }); } }); } void LoadStoreTestbench::add_memory(physaddr start, const std::vector<uint8_t> &bytes) { auto addr = start; for (auto b : bytes) mem_bytes[addr++] = b; } TEST_F(LoadStoreTestbench, SegmentCalculation) { this->dut.segment = 0x800; write_mar(0x100); cycle(); ASSERT_EQ(this->dut.m_addr, ((0x800 << 4) + 0x100LU) >> 1); } TEST_F(LoadStoreTestbench, SegmentNoWrap) { this->dut.segment = 0; add_memory(0xfffe, {0xee, 0x55}); add_memory(0x10000, {0xaa, 0xff}); write_mar(0xffff); read(WIDTH_16); ASSERT_EQ(get_read_values(), std::vector<uint16_t>{0xaa55}); } struct LoadParams { physaddr mar_value; LoadStoreTestbench::MemWidth width; std::vector<uint16_t> values; }; class LoadTest : public LoadStoreTestbench, public ::testing::WithParamInterface<LoadParams> { }; TEST_P(LoadTest, LoadMemory) { auto params = GetParam(); add_memory(0xff, {0xff, 0x55, 0xaa, 0xff}); write_mar(params.mar_value); read(params.width); ASSERT_EQ(get_read_values(), params.values); } INSTANTIATE_TEST_CASE_P( Load, LoadTest, ::testing::Values( LoadParams{0x100U, LoadStoreTestbench::MemWidth::WIDTH_16, {0xaa55}}, LoadParams{0x101U, LoadStoreTestbench::MemWidth::WIDTH_16, {0xffaa}}, LoadParams{0x100U, LoadStoreTestbench::MemWidth::WIDTH_8, {0x55}}, LoadParams{0x101U, LoadStoreTestbench::MemWidth::WIDTH_8, {0xaa}})); struct StoreParams { physaddr mar_value; uint16_t mdr_value; LoadStoreTestbench::MemWidth width; std::map<physaddr, uint8_t> expected_mem; }; class StoreTest : public LoadStoreTestbench, public ::testing::WithParamInterface<StoreParams> { }; TEST_P(StoreTest, StoreMemory) { auto params = GetParam(); write_mar(params.mar_value); write_mdr(params.mdr_value); write(params.width); ASSERT_TRUE(params.expected_mem.size() == mem_bytes.size() && std::equal(params.expected_mem.begin(), params.expected_mem.end(), mem_bytes.begin())); } INSTANTIATE_TEST_CASE_P( Store, StoreTest, ::testing::Values(StoreParams{0x100U, 0xaa55, LoadStoreTestbench::MemWidth::WIDTH_16, {{0x100, 0x55}, {0x101, 0xaa}}}, StoreParams{0x101U, 0xaa55, LoadStoreTestbench::MemWidth::WIDTH_16, {{0x101, 0x55}, {0x102, 0xaa}}}, StoreParams{0x100U, 0xff55, LoadStoreTestbench::MemWidth::WIDTH_8, {{0x100, 0x55}}}, StoreParams{0x101U, 0xff55, LoadStoreTestbench::MemWidth::WIDTH_8, {{0x101, 0x55}}}));