Hone logo
Hone
Problems

Simulating Memory-Mapped I/O in Go

This challenge focuses on understanding and implementing the concept of memory-mapped I/O within a Go program. Memory-mapped I/O is a technique where hardware device registers are accessed as if they were regular memory locations, simplifying device interaction. You will simulate this by creating a virtual memory space that can be read from and written to, mimicking how a program would interact with physical hardware.

Problem Description

Your task is to create a Go program that simulates memory-mapped I/O. You will need to implement a mechanism to represent a contiguous block of memory that can be accessed at specific addresses. This simulated memory will be used to interact with virtual "hardware devices" represented by different sections of this memory.

Key Requirements:

  1. Virtual Memory Space: Create a byte slice in Go that represents your simulated memory space. This slice will be the core of your memory-mapped I/O.
  2. Device Mapping: Define different "devices" by assigning them specific address ranges within the virtual memory space.
  3. Read/Write Operations: Implement functions to read from and write to specific addresses within the virtual memory space. These functions should handle:
    • Reading a single byte.
    • Reading a fixed-size integer (e.g., uint32).
    • Writing a single byte.
    • Writing a fixed-size integer (e.g., uint32).
  4. Address Validation: Ensure that all read and write operations are within the bounds of the virtual memory space and that they align correctly with integer sizes (e.g., a uint32 read/write should start at an address divisible by 4).
  5. Device Interaction (Simulated): Design a simple scenario where reading from or writing to a specific memory address triggers a predefined behavior associated with a "device." For instance, writing a specific value to a "control register" might toggle a "status flag" that can be read back.

Expected Behavior:

  • When a byte or integer is written to a valid address, the corresponding bytes in the virtual memory slice should be updated.
  • When a byte or integer is read from a valid address, the correct data should be returned from the virtual memory slice.
  • Attempting to read or write outside the memory bounds or at misaligned addresses should result in an error.
  • Simulated device interactions should respond as defined (e.g., status flags changing based on control register writes).

Edge Cases to Consider:

  • Writing data that spans across the boundary of mapped device regions.
  • Reading data that spans across the boundary of mapped device regions.
  • Accessing the very beginning or end of the memory space.
  • Concurrent access to the memory space (though for this challenge, you can assume single-threaded access unless specified otherwise).

Examples

Example 1: Basic Byte Read/Write

Input:

  • Memory size: 256 bytes
  • Write 0xAB to address 0x10.
  • Read byte from address 0x10.

Output:

  • Read value: 0xAB

Explanation: The byte 0xAB is written to the virtual memory at offset 0x10. A subsequent read from the same address correctly retrieves 0xAB.

Example 2: uint32 Read/Write

Input:

  • Memory size: 1024 bytes
  • Write the uint32 value 0x12345678 to address 0x50.
  • Read uint32 from address 0x50.

Output:

  • Read value: 0x12345678

Explanation: The 4 bytes representing 0x12345678 are written to memory starting at 0x50. The read operation reconstructs the uint32 from these bytes. (Assume little-endian byte order for this example).

Example 3: Simulated Device Interaction

Input:

  • Memory size: 512 bytes
  • Device "LED Controller" mapped to addresses 0x100 (control register) and 0x104 (status register).
  • Writing 0x01 to 0x100 should turn on the "LED" (set bit 0 in the status register at 0x104).
  • Writing 0x00 to 0x100 should turn off the "LED" (clear bit 0 in the status register at 0x104).
  • Write 0x01 to address 0x100.
  • Read uint32 from address 0x104.
  • Write 0x00 to address 0x100.
  • Read uint32 from address 0x104.

Output:

  • First read: 0x00000001
  • Second read: 0x00000000

Explanation: Writing to the control register (0x100) influences the value at the status register (0x104). The first write (0x01) sets bit 0 of the status, and the second write (0x00) clears it, demonstrating a basic device behavior.

Constraints

  • Memory Size: The virtual memory space will be between 1KB and 1MB.
  • Addresses: Addresses will be within the bounds of the memory space.
  • Data Types: You should support reading and writing bytes (uint8) and 32-bit unsigned integers (uint32).
  • Alignment: Reads and writes of uint32 must be aligned to a 4-byte boundary (i.e., the address must be divisible by 4).
  • Performance: While not strictly enforced with tests, aim for efficient memory access patterns.
  • Endianness: Assume little-endian byte order for multi-byte integer operations.

Notes

  • You'll likely need to use Go's unsafe package to directly manipulate byte slices and manage memory addresses, but do so cautiously and with an understanding of its implications.
  • Consider how you will map devices to specific regions of the memory. This might involve a struct or a map to store device configurations.
  • The simulation of device behavior is intentionally simple. Focus on the core mechanics of memory mapping.
  • Think about how you would represent an error condition (e.g., invalid address, misaligned access). Go's error handling mechanisms will be useful here.
  • For the simulated device interaction, you can implement the device logic directly within your memory access functions.
Loading editor...
go