Skip to content

Commit 5240640

Browse files
committed
Multi mcu support
1 parent c1b6bfe commit 5240640

File tree

2 files changed

+215
-22
lines changed

2 files changed

+215
-22
lines changed

sim/aviron/src/lib/mcu.zig

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
const std = @import("std");
2+
const io_mod = @import("io.zig");
3+
const Cpu = @import("Cpu.zig");
4+
5+
/// MCU configuration defines the memory layout and special register addresses
6+
/// for a specific AVR microcontroller.
7+
pub const Config = struct {
8+
/// Human-readable name of the MCU
9+
name: []const u8,
10+
11+
/// Flash memory size in bytes (must be 2-aligned)
12+
flash_size: usize,
13+
14+
/// SRAM size in bytes
15+
sram_size: usize,
16+
17+
/// SRAM base address in data space
18+
sram_base: u24,
19+
20+
/// EEPROM size in bytes
21+
eeprom_size: usize,
22+
23+
/// Code model (determines PC width)
24+
code_model: Cpu.CodeModel,
25+
26+
/// Instruction set variant
27+
instruction_set: Cpu.InstructionSet,
28+
29+
/// Special I/O register addresses
30+
special_io: SpecialIoConfig,
31+
32+
/// Memory mapper configuration
33+
mapper_config: MapperConfig,
34+
};
35+
36+
pub const SpecialIoConfig = struct {
37+
/// RAMP registers for extended addressing (if present)
38+
ramp_x: ?io_mod.IO.Address = null,
39+
ramp_y: ?io_mod.IO.Address = null,
40+
ramp_z: ?io_mod.IO.Address = null,
41+
ramp_d: ?io_mod.IO.Address = null,
42+
43+
/// Extended indirect register (if present)
44+
e_ind: ?io_mod.IO.Address = null,
45+
46+
/// Stack pointer registers
47+
sp_l: io_mod.IO.Address,
48+
sp_h: io_mod.IO.Address,
49+
50+
/// Status register
51+
sreg: io_mod.IO.Address,
52+
};
53+
54+
/// Configuration for the memory mapper
55+
pub const MapperConfig = struct {
56+
/// Function to translate data-space address to IO address
57+
/// Returns null if the address is not mapped to IO
58+
io_translate: *const fn (data_addr: u24) ?io_mod.IO.Address,
59+
60+
/// Base address where SRAM is mapped in data space
61+
sram_base: u24,
62+
};
63+
64+
/// Classic AVR memory layout: IO mapped at 0x0020-0x005F in data space, SRAM at 0x0100+
65+
pub fn ClassicMapperConfig(comptime sram_base: u24) type {
66+
return struct {
67+
pub fn io_translate(data_addr: u24) ?io_mod.IO.Address {
68+
// Classic AVR: IO registers at 0x0020-0x005F map to IO addresses 0x00-0x3F
69+
return if (data_addr >= 0x20 and data_addr <= 0x5F)
70+
@intCast(data_addr - 0x20)
71+
else
72+
null;
73+
}
74+
75+
pub const config = MapperConfig{
76+
.io_translate = io_translate,
77+
.sram_base = sram_base,
78+
};
79+
};
80+
}
81+
82+
/// Extended IO mapping for devices with more IO registers
83+
pub fn ExtendedMapperConfig(comptime sram_base: u24) type {
84+
return struct {
85+
pub fn io_translate(data_addr: u24) ?io_mod.IO.Address {
86+
// Extended IO: 0x0020-0x00FF map to IO addresses 0x00-0xDF
87+
return if (data_addr >= 0x20 and data_addr <= 0xFF)
88+
@intCast(data_addr - 0x20)
89+
else
90+
null;
91+
}
92+
93+
pub const config = MapperConfig{
94+
.io_translate = io_translate,
95+
.sram_base = sram_base,
96+
};
97+
};
98+
}
99+
100+
// ============================================================================
101+
// Predefined MCU Configurations
102+
// ============================================================================
103+
104+
/// ATmega328P configuration (Arduino Uno)
105+
pub const atmega328p = Config{
106+
.name = "ATmega328P",
107+
.flash_size = 32768,
108+
.sram_size = 2048,
109+
.sram_base = 0x0100,
110+
.eeprom_size = 1024,
111+
.code_model = .code16,
112+
.instruction_set = .avr5,
113+
.special_io = .{
114+
.ramp_x = null,
115+
.ramp_y = null,
116+
.ramp_z = null,
117+
.ramp_d = null,
118+
.e_ind = null,
119+
.sp_l = 0x3D,
120+
.sp_h = 0x3E,
121+
.sreg = 0x3F,
122+
},
123+
.mapper_config = .{
124+
.io_translate = ClassicMapperConfig(0x0100).config.io_translate,
125+
.sram_base = 0x0100,
126+
},
127+
};
128+
129+
/// ATtiny816 configuration (modern tinyAVR)
130+
pub const attiny816 = Config{
131+
.name = "ATtiny816",
132+
.flash_size = 8192,
133+
.sram_size = 512,
134+
.sram_base = 0x3F00, // High memory for tinyAVR 0/1/2 series
135+
.eeprom_size = 128,
136+
.code_model = .code16,
137+
.instruction_set = .avr5, // Actually uses AVRe/AVRe+ but avr5 is close enough
138+
.special_io = .{
139+
.ramp_x = null,
140+
.ramp_y = null,
141+
.ramp_z = null,
142+
.ramp_d = null,
143+
.e_ind = null,
144+
.sp_l = 0x3D,
145+
.sp_h = 0x3E,
146+
.sreg = 0x3F,
147+
},
148+
.mapper_config = .{
149+
.io_translate = ClassicMapperConfig(0x3F00).config.io_translate,
150+
.sram_base = 0x3F00,
151+
},
152+
};
153+
154+
/// ATmega2560 configuration (Arduino Mega)
155+
pub const atmega2560 = Config{
156+
.name = "ATmega2560",
157+
.flash_size = 262144,
158+
.sram_size = 8192,
159+
.sram_base = 0x0200,
160+
.eeprom_size = 4096,
161+
.code_model = .code22,
162+
.instruction_set = .avr6,
163+
.special_io = .{
164+
.ramp_x = null,
165+
.ramp_y = null,
166+
.ramp_z = 0x3B, // RAMPZ for extended addressing
167+
.ramp_d = 0x38, // RAMPD
168+
.e_ind = 0x3C, // EIND
169+
.sp_l = 0x3D,
170+
.sp_h = 0x3E,
171+
.sreg = 0x3F,
172+
},
173+
.mapper_config = .{
174+
.io_translate = ExtendedMapperConfig(0x0200).config.io_translate,
175+
.sram_base = 0x0200,
176+
},
177+
};
178+
179+
/// Lookup MCU configuration by name
180+
pub fn getConfig(name: []const u8) ?Config {
181+
const configs = .{
182+
atmega328p,
183+
attiny816,
184+
atmega2560,
185+
};
186+
187+
inline for (configs) |config| {
188+
if (std.mem.eql(u8, config.name, name)) {
189+
return config;
190+
}
191+
}
192+
193+
return null;
194+
}

sim/aviron/src/main.zig

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,21 @@ pub fn main() !u8 {
2121
return if (cli.options.help) @as(u8, 0) else 1;
2222
}
2323

24-
// Emulate Atmega382p device size:
25-
24+
// Get MCU configuration
2625
// TODO: Add support for more MCUs!
2726
std.debug.assert(cli.options.mcu == .atmega328p);
27+
const mcu_config = aviron.mcu.atmega328p;
2828

29-
const sram_base: u24 = 0x0100; // ATmega328P SRAM starts at 0x0100
29+
// Allocate memory based on MCU configuration
30+
var flash_storage = aviron.Flash.Static(mcu_config.flash_size){};
31+
var sram = aviron.RAM.Static(mcu_config.sram_size){};
32+
var eeprom = aviron.EEPROM.Static(mcu_config.eeprom_size){};
3033

31-
var flash_storage = aviron.Flash.Static(32768){};
32-
var sram = aviron.RAM.Static(2048){};
33-
var eeprom = aviron.EEPROM.Static(1024){};
34-
// AVR data space: registers + I/O (0x0000..0x00FF), SRAM base is device-specific (0x0100 on ATmega328P).
34+
// AVR data space: registers + I/O (0x0000..0x00FF), SRAM base is device-specific.
3535
// Stack pointer must be initialized to RAMEND (top of SRAM) at reset.
36-
// For ATmega328P with 2KB SRAM, RAMEND = 0x08FF.
3736
var io = IO{
3837
.sreg = undefined,
39-
.sp = sram_base + @as(u16, sram.data.len - 1),
38+
.sp = mcu_config.sram_base + @as(u16, mcu_config.sram_size - 1),
4039
};
4140

4241
// Create memory interfaces
@@ -46,7 +45,7 @@ pub fn main() !u8 {
4645
var io_mem = io.memory();
4746

4847
// Create mapper that routes addresses to IO or SRAM (using pointers to avoid duplication)
49-
const MapperConfig = aviron.mcu.ClassicMapperConfig(sram_base);
48+
const MapperConfig = aviron.mcu.ClassicMapperConfig(mcu_config.sram_base);
5049
const MapperImpl = aviron.Mapper.SimpleMapper(MapperConfig);
5150
var memory_mapper = MapperImpl{
5251
.io = &io_mem,
@@ -58,25 +57,25 @@ pub fn main() !u8 {
5857

5958
.flash = flash_mem,
6059
.sram = sram_mem,
61-
.sram_base = sram_base,
60+
.sram_base = mcu_config.sram_base,
6261
.eeprom = eeprom_mem,
6362
.io = io_mem,
6463
.mapper = memory_mapper.mapper(),
6564

66-
.code_model = .code16,
67-
.instruction_set = .avr5,
65+
.code_model = mcu_config.code_model,
66+
.instruction_set = mcu_config.instruction_set,
6867

6968
.sio = .{
70-
.ramp_x = null,
71-
.ramp_y = null,
72-
.ramp_z = null,
73-
.ramp_d = null,
74-
.e_ind = null,
69+
.ramp_x = mcu_config.special_io.ramp_x,
70+
.ramp_y = mcu_config.special_io.ramp_y,
71+
.ramp_z = mcu_config.special_io.ramp_z,
72+
.ramp_d = mcu_config.special_io.ramp_d,
73+
.e_ind = mcu_config.special_io.e_ind,
7574

76-
.sp_l = @intFromEnum(IO.Register.sp_l),
77-
.sp_h = @intFromEnum(IO.Register.sp_h),
75+
.sp_l = mcu_config.special_io.sp_l,
76+
.sp_h = mcu_config.special_io.sp_h,
7877

79-
.sreg = @intFromEnum(IO.Register.sreg),
78+
.sreg = mcu_config.special_io.sreg,
8079
},
8180
};
8281

@@ -118,7 +117,7 @@ pub fn main() !u8 {
118117

119118
const addr_masked: u24 = @intCast(phdr.p_paddr & 0x007F_FFFF);
120119
const target_addr: u24 = if (phdr.p_paddr >= 0x0080_0000)
121-
addr_masked - sram_base
120+
addr_masked - mcu_config.sram_base
122121
else
123122
addr_masked; // Flash always starts at 0
124123

0 commit comments

Comments
 (0)