Hone logo
Hone
Problems

Simulating Memory-Mapped I/O in Go

Memory-mapped I/O (MMIO) is a technique where physical I/O devices are accessed by mapping them into the process's address space. This allows the device to be treated as if it were regular memory, simplifying device interaction. This challenge asks you to simulate this concept in Go, creating a system where you can read and write to specific memory addresses that represent device registers.

Problem Description

You are tasked with creating a simplified memory-mapped I/O system in Go. This system will consist of a fixed-size memory space (represented as a slice of bytes) and a few "devices" mapped to specific regions within that memory space. Your program should allow users to read and write to these memory locations, simulating the interaction with hardware registers.

What needs to be achieved:

  1. Memory Space: Create a byte slice representing the memory space.
  2. Device Mapping: Define a few "devices" (e.g., a UART, a GPIO controller) and map them to specific ranges within the memory space. Each device will have a few registers (also represented as byte slices).
  3. Read/Write Functions: Implement functions read_memory(address uint32) byte and write_memory(address uint32, value byte) that simulate reading from and writing to the memory space. These functions should:
    • Check if the address is within the valid memory range.
    • If the address falls within a device's memory region, access the appropriate register within that device.
    • If the address is outside any device's region, return a default value (e.g., 0) for reads and ignore writes.
  4. Device Definitions: Define at least two devices:
    • UART (Universal Asynchronous Receiver/Transmitter): Should have registers for:
      • UART_DATA: Data register (read/write)
      • UART_STATUS: Status register (read-only) - Simulate a few status bits (e.g., bit 0: transmit complete, bit 1: receive complete).
    • GPIO (General Purpose Input/Output): Should have registers for:
      • GPIO_DATA: Data register (read/write) - Controls the state of GPIO pins.
      • GPIO_INPUT: Input register (read-only) - Simulates reading the state of input pins.

Key Requirements:

  • The memory addresses should be represented as uint32.
  • The device mappings should be clearly defined.
  • The read and write functions should handle out-of-bounds access gracefully.
  • The code should be well-structured and easy to understand.

Expected Behavior:

  • Reading from a valid memory address within a device's register should return the current value of that register.
  • Writing to a valid memory address within a device's register should update the value of that register.
  • Reading from an invalid memory address should return a default value (0).
  • Writing to an invalid memory address should be ignored.
  • The status register of the UART should be updated to reflect simulated events (e.g., setting the transmit complete bit after a write to the data register).
  • The GPIO registers should behave as expected for input and output.

Edge Cases to Consider:

  • Addresses outside the defined memory range.
  • Simultaneous reads and writes to the same register.
  • Handling of bit fields within registers (although full bit manipulation is not required, consider how you would access individual bits).

Examples

Example 1:

Input: address = 0x1000, value = 0xAA
Output: No output (write operation)
Explanation: 0x1000 falls within the UART_DATA register. The value 0xAA is written to the UART_DATA register.

Example 2:

Input: address = 0x2000
Output: 0x00
Explanation: 0x2000 falls within the GPIO_INPUT register. The current state of the input pins is read (simulated as 0x00).

Example 3:

Input: address = 0xFFFFFFFF, value = 0x55
Output: No output (write operation)
Explanation: 0xFFFFFFFF is outside the valid memory range. The write operation is ignored.

Constraints

  • Memory Size: The total memory space should be at least 4096 bytes (4KB).
  • Device Mapping: Each device should occupy at least 256 bytes of memory.
  • Address Range: All addresses must be within the range of 0 to the memory size - 1.
  • Performance: The read and write functions should be efficient enough to handle a reasonable number of requests (e.g., 10000) within a short time frame (e.g., 1 second). Optimization is not the primary focus, but avoid unnecessary overhead.

Notes

  • This is a simulation, so you don't need to interact with actual hardware.
  • Focus on the logical structure and correctness of the memory-mapped I/O system.
  • Consider using constants to define memory addresses and device mappings for better readability and maintainability.
  • You can use helper functions to encapsulate the logic for accessing individual registers within a device.
  • Think about how you would extend this system to support more devices and registers in the future.
  • The status register of the UART can be simplified to just a few bits to represent basic status conditions. No complex UART protocol implementation is required.
Loading editor...
go