diff --git a/Makefile b/Makefile index 12ccbed..57c28cb 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ TOPTARGETS = build clean IMAGES = common \ cpu/code-in-scratchpad \ + dma/otc-test \ gpu/bandwidth \ gpu/benchmark \ gpu/quad \ diff --git a/README.md b/README.md index b9cafd4..d8abbcc 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ Name | Description -------------------------|------------ code-in-scratchpad | **(Not finished)** Check whether code execution from Scratchpad is possible +### DMA + +Name | Description +-------------------------|------------ +otc-test | DMA Channel 6 (OTC aka Ordering Table clear) unit tests + ### GPU Name | Description diff --git a/common.mk b/common.mk index 941834a..066cb1b 100644 --- a/common.mk +++ b/common.mk @@ -5,8 +5,8 @@ LIBDIRS := -L$(PSN00BSDK)/libpsn00b GCC_VERSION = 7.4.0 GCC_BASE = /usr/local/mipsel-unknown-elf -CFLAGS ?= -g -msoft-float -O2 -fno-builtin -fdata-sections -ffunction-sections -Wall -Wextra -Wno-strict-aliasing -Wno-sign-compare -CPPFLAGS ?= $(CFLAGS) -fno-exceptions -std=c++17 +CFLAGS ?= -g -msoft-float -O3 -fno-builtin -fdata-sections -ffunction-sections -Wall -Wextra -Wno-strict-aliasing -Wno-sign-compare +CPPFLAGS ?= $(CFLAGS) -fno-exceptions -std=c++1z AFLAGS ?= -g -msoft-float LDFLAGS ?= -g -Ttext=0x80010000 -gc-sections -T $(GCC_BASE)/mipsel-unknown-elf/lib/ldscripts/elf32elmip.x diff --git a/common/dma.hpp b/common/dma.hpp new file mode 100644 index 0000000..673c72a --- /dev/null +++ b/common/dma.hpp @@ -0,0 +1,86 @@ +#pragma once +#include + +// Copied from Avocado +namespace DMA { +enum class Channel { MDECin, MDECout, GPU, CDROM, SPU, PIO, OTC }; + +const inline uint32_t CH_BASE_ADDR = 0x1F801080; +const inline uint32_t CH_BLOCK_ADDR = 0x1F801084; +const inline uint32_t CH_CONTROL_ADDR = 0x1F801088; +const inline uint32_t CONTROL_ADDR = 0x1F8010F0; + +// DMA base address +union MADDR { + struct { + uint32_t address : 24; + uint32_t : 8; + }; + uint32_t _reg; + uint8_t _byte[4]; + + MADDR() : _reg(0) {} +}; + +// DMA Block Control +union BCR { + union { + struct { + uint32_t wordCount : 16; + uint32_t : 16; + } syncMode0; + struct { + uint32_t blockSize : 16; + uint32_t blockCount : 16; + } syncMode1; + }; + uint32_t _reg; + uint8_t _byte[4]; + + BCR() : _reg(0) {} +}; + +// DMA Channel Control +union CHCR { + enum class Direction : uint32_t { toRam = 0, fromRam = 1 }; + enum class MemoryAddressStep : uint32_t { forward = 0, backward = 1 }; + enum class SyncMode : uint32_t { startImmediately = 0, syncBlockToDmaRequests = 1, linkedListMode = 2, reserved = 3 }; + enum class ChoppingEnable : uint32_t { normal = 0, chopping = 1 }; + enum class Enabled : uint32_t { completed = 0, stop = 0, start = 1 }; + enum class StartTrigger : uint32_t { clear = 0, automatic = 0, manual = 1 }; + + struct { + Direction direction : 1; + MemoryAddressStep memoryAddressStep : 1; + uint32_t : 6; + uint32_t choppingEnable : 1; + SyncMode syncMode : 2; + uint32_t : 5; + uint32_t choppingDmaWindowSize : 3; // Chopping DMA Window Size (1 SHL N words) + uint32_t : 1; + uint32_t choppingCpuWindowSize : 3; // Chopping CPU Window Size(1 SHL N clks) + uint32_t : 1; + Enabled enabled : 1; // stopped/completed, start/enable/busy + uint32_t : 3; + StartTrigger startTrigger : 1; + uint32_t : 3; + }; + uint32_t _reg; + uint8_t _byte[4]; + + CHCR() : _reg(0) {} + + static CHCR OTC() { + CHCR control; + control.direction = Direction::toRam; + control.memoryAddressStep = MemoryAddressStep::backward; + control.choppingEnable = 0; + control.syncMode = SyncMode::startImmediately; + control.choppingDmaWindowSize = 0; + control.choppingCpuWindowSize = 0; + control.enabled = Enabled::start; + control.startTrigger = StartTrigger::manual; + return control; + } +}; +}; diff --git a/common/test.c b/common/test.c new file mode 100644 index 0000000..4763349 --- /dev/null +++ b/common/test.c @@ -0,0 +1,3 @@ +#include "test.h" + +struct TEST __test = {0}; \ No newline at end of file diff --git a/common/test.h b/common/test.h index 8ec5420..1b38780 100644 --- a/common/test.h +++ b/common/test.h @@ -1,14 +1,53 @@ #pragma once +#include "stdint.h" -#define assertEquals(given, expected) \ -do { \ - auto GIVEN = (given); \ - auto EXPECTED = (expected); \ - if (GIVEN == EXPECTED) { \ - printf("pass - %s\n", __FUNCTION__); \ - } else { \ - printf("fail - %s:%d `"#given" == "#expected"`," \ - " given: 0x%x, expected: 0x%x\n", \ - __FUNCTION__, __LINE__, GIVEN, EXPECTED); \ - } \ -} while(0) +#ifdef __cplusplus +extern "C" { +#endif + +struct TEST { + bool quiet; + int failedAssertions; + int passedAssertions; +}; + +extern struct TEST __test; +#ifdef __cplusplus +} +#endif + +#define assertEquals(given, expected) \ +[](auto FUNCTION, auto GIVEN, auto EXPECTED) -> bool { \ + if (GIVEN == EXPECTED) { \ + __test.passedAssertions++; \ + if (!__test.quiet) { \ + printf("pass - %s\n", FUNCTION); \ + } \ + return true; \ + } else { \ + __test.failedAssertions++; \ + printf("fail - %s:%d `"#given" == "#expected"`," \ + " given: 0x%x, expected: 0x%x\n", \ + FUNCTION, __LINE__, GIVEN, EXPECTED); \ + return false; \ + } \ +}(__FUNCTION__, given, expected) + +#define TEST_MULTIPLE_BEGIN() \ +[]() { \ + __test.quiet = true; \ +}() + +#define TEST_MULTIPLE_END() \ +[](auto FUNCTION) { \ + bool passed = __test.failedAssertions == 0; \ + __test.quiet = false; \ + __test.failedAssertions = 0; \ + __test.passedAssertions = 0; \ + if (passed) { \ + printf("pass - %s\n", FUNCTION); \ + return true; \ + } else { \ + return false; \ + } \ +}(__FUNCTION__) diff --git a/dma/otc-test/Makefile b/dma/otc-test/Makefile new file mode 100644 index 0000000..7d72f7f --- /dev/null +++ b/dma/otc-test/Makefile @@ -0,0 +1,3 @@ +TARGET = otc-test.elf + +include ../../common-test.mk \ No newline at end of file diff --git a/dma/otc-test/main.cpp b/dma/otc-test/main.cpp new file mode 100644 index 0000000..22777bf --- /dev/null +++ b/dma/otc-test/main.cpp @@ -0,0 +1,310 @@ +#include +#include +#include +#include +#include +#include +#include "otc.h" + +using namespace DMA; + +constexpr uint32_t DMA_OTC_CTRL = DMA::CH_CONTROL_ADDR + 0x10 * (int)Channel::OTC; + +// Check DMA6 OTC with standard parameters +// DMA should create a reverse linked list where nth item points to n-1 item +// and item 0 is terminated with 0xffffff +void testOtcStandard() { + volatile uint32_t buf[4] = { + 0x11111111, + 0x22222222, + 0x33333333, + 0x44444444 + }; + + auto control = CHCR::OTC(); + setupDmaOtc((uint32_t)&buf[3], 4, control); + + TEST_MULTIPLE_BEGIN(); + assertEquals(buf[3], ((uint32_t)&buf[2]) & 0xffffff); + assertEquals(buf[2], ((uint32_t)&buf[1]) & 0xffffff); + assertEquals(buf[1], ((uint32_t)&buf[0]) & 0xffffff); + assertEquals(buf[0], 0xffffff); + TEST_MULTIPLE_END(); +} + +// Check Transfer with size == 0 +void testOtcBigTransfer() { + const int SIZE = 64 * 1024; + uint32_t* buf = (uint32_t*)malloc(sizeof(uint32_t) * SIZE); + for (int i = 0; i + +using namespace DMA; + +void waitForDma(Channel ch) { + volatile CHCR* control = (CHCR*)(0x1F801088 + 0x10 * (int)ch); + + while ( + control->startTrigger == CHCR::StartTrigger::manual && + control->enabled != CHCR::Enabled::completed + ); +} + +// Separate compilation unit for these functions to prevent inlining +void setupDmaOtc(uint32_t address, uint16_t wordCount, CHCR control) { + MADDR addr; + addr.address = address; + + BCR block; + block.syncMode0.wordCount = wordCount; + + waitForDma(Channel::OTC); + + write32(CH_BASE_ADDR + 0x10 * (int)Channel::OTC, addr._reg); + write32(CH_BLOCK_ADDR + 0x10 * (int)Channel::OTC, block._reg); + write32(CH_CONTROL_ADDR + 0x10 * (int)Channel::OTC, control._reg); +} + +void dmaMasterEnable(Channel ch, bool enabled) { + uint32_t dmaControl = read32(CONTROL_ADDR); + uint32_t mask = 0b1000 << ((int)ch * 4); + if (enabled) { + dmaControl |= mask; + } else { + dmaControl &= ~mask; + } + write32(CONTROL_ADDR, dmaControl); +} \ No newline at end of file diff --git a/dma/otc-test/otc.h b/dma/otc-test/otc.h new file mode 100644 index 0000000..8ec7a88 --- /dev/null +++ b/dma/otc-test/otc.h @@ -0,0 +1,5 @@ +#include + +void waitForDma(DMA::Channel ch); +void setupDmaOtc(uint32_t address, uint16_t wordCount, DMA::CHCR control); +void dmaMasterEnable(DMA::Channel ch, bool enabled); \ No newline at end of file diff --git a/dma/otc-test/psx.log b/dma/otc-test/psx.log new file mode 100644 index 0000000..1c74834 --- /dev/null +++ b/dma/otc-test/psx.log @@ -0,0 +1,15 @@ +dma/otc-test (DMA Channel 6) +pass - testOtcStandard +pass - testOtcWontStartOnAutomaticMode +pass - testOtcWontStartWithoutStartFlag +pass - testOtcForward +pass - testOtcFromRam +pass - testOtcSyncModeBlocks +pass - testOtcSyncModeLinkedList +pass - testOtcSyncModeReserved +pass - testOtcWhichBitsAreHardwiredToZero +pass - testOtcWhichBitsAreHardwiredToOne +pass - testOtcUnusedControlBitsAreAlwaysZero +pass - testOtcControlBitsAfterTransfer +pass - testOtcStandardWithMasterDisabled +Done. \ No newline at end of file