-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuild.zig
241 lines (207 loc) · 7.78 KB
/
build.zig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
const std = @import("std");
pub fn build(b: *std.Build) void {
_ = b;
}
pub fn init(b: *std.Build, options: UniMicroOptions) *std.Build.Step.InstallFile {
// Determine the architecture to be used.
const target = b.resolveTargetQuery(options.target_platform.cpu.target);
// Compile the ELF
const elf = b.addExecutable(.{
.name = b.fmt("{s}.elf", .{options.name}),
.root_source_file = .{ .cwd_relative = root() ++ "/main.zig" },
.target = target,
.optimize = options.optimization_level,
.single_threaded = options.target_platform.single_threaded,
.linkage = .static,
});
// Set the entrypoint to the unimicro_main function. It prepares everything
// and then calls the user's main function.
elf.entry = .{ .symbol_name = "unimicro_main" };
const default_interrupts_vector = b.addObject(.{
.name = "default_interrupts_vector",
.root_source_file = .{
.cwd_relative = b.fmt("{s}/hal/{s}/{s}/default_interrupts.zig", .{
root(),
options.target_platform.manufacturer,
options.target_platform.name,
}),
},
.target = target,
.optimize = options.optimization_level,
.single_threaded = options.target_platform.single_threaded,
});
elf.addObject(default_interrupts_vector);
// Module with the HAL to be imported by the user.
const hal_module = b.createModule(.{
.root_source_file = .{
.cwd_relative = b.fmt("{s}/hal/{s}/{s}/hal.zig", .{
root(),
options.target_platform.manufacturer,
options.target_platform.name,
}),
},
.target = target,
.optimize = options.optimization_level,
.single_threaded = options.target_platform.single_threaded,
});
// Add the app module as an import on the main.zig file.
elf.root_module.addImport("UniMicro", hal_module);
// Create an app module containing the user's code.
const app_module = b.createModule(.{
.root_source_file = options.main_file,
.target = target,
.optimize = options.optimization_level,
.single_threaded = options.target_platform.single_threaded,
.imports = &.{
.{ .name = "UniMicro", .module = hal_module },
},
});
// Add the app module as an import on the main.zig file.
elf.root_module.addImport("app", app_module);
// Create a module containing the functions startup and hang, which are
// processor specific and, threrefore, come from the cpus/ directory.
const cpu_module = b.createModule(.{
.root_source_file = .{ .cwd_relative = b.fmt("{s}/cpus/{s}/{s}.zig", .{
root(),
options.target_platform.cpu.manufacturer,
options.target_platform.cpu.name,
}) },
.target = target,
.optimize = options.optimization_level,
.single_threaded = options.target_platform.single_threaded,
});
// Add the cpu module as an import on the main.zig file.
elf.root_module.addImport("cpu", cpu_module);
// Create the executable that generates the linker script.
const linker_gen = b.addExecutable(.{
.name = "linker_generator",
.root_source_file = .{ .cwd_relative = root() ++ "/utils/linker_generator.zig" },
.target = b.graph.host,
.optimize = std.builtin.OptimizeMode.ReleaseFast,
});
// Add an import "UniMicro" to the linker_generator executable.
linker_gen.root_module.addImport("UniMicro/build", b.createModule(.{
.root_source_file = .{ .cwd_relative = root() ++ "/build.zig" },
}));
// Stringify the args to be passed to the linker_generator.
const args_str = std.json.stringifyAlloc(b.allocator, options.target_platform, .{}) catch @panic("Out of Memory!");
// Run the linker_generator.
const linker_gen_run = b.addRunArtifact(linker_gen);
linker_gen_run.addArg(args_str);
// Set the linker script of the ELF to be that generated by the
// linker generator executable.
elf.setLinkerScript(linker_gen_run.addOutputFileArg("memory.ld"));
// Copy elf to output dir
const copy_elf = b.addInstallArtifact(elf, .{});
b.default_step.dependOn(©_elf.step);
// Make a bin out of elf
const bin = b.addObjCopy(elf.getEmittedBin(), .{
.format = .bin,
});
bin.step.dependOn(©_elf.step);
// Copy bin to the output directory
const copy_bin = b.addInstallBinFile(bin.getOutput(), b.fmt("{s}.bin", .{options.name}));
b.default_step.dependOn(©_bin.step);
return copy_bin;
}
inline fn root() []const u8 {
return comptime (std.fs.path.dirname(@src().file) orelse ".");
}
pub const UniMicroOptions = struct {
name: []const u8,
main_file: std.Build.LazyPath,
optimization_level: std.builtin.OptimizeMode,
target_platform: Chip,
};
pub const supported_chips = .{
.stmicro = .{
.stm32f411cc = Chip{
.manufacturer = "stmicro",
.name = "stm32f411cc",
.cpu = supported_cpus.arm.cortex_m4f,
.memory_sections = &.{
MemorySection{ .origin = 0x0800_0000, .length = 256 * Units.KB, .type = .FLASH },
MemorySection{ .origin = 0x2000_0000, .length = 128 * Units.KB, .type = .RAM },
},
},
},
};
pub const supported_cpus = .{
.arm = .{
.cortex_m4 = Cpu{
.manufacturer = "arm",
.name = "cortex_m4",
.target = .{
.cpu_model = std.zig.CrossTarget.CpuModel{ .explicit = &std.Target.arm.cpu.cortex_m4 },
.cpu_arch = .thumb,
.os_tag = .freestanding,
.abi = .none,
},
},
.cortex_m4f = Cpu{
.manufacturer = "arm",
.name = "cortex_m4",
.target = .{
.cpu_model = std.zig.CrossTarget.CpuModel{ .explicit = &std.Target.arm.cpu.cortex_m4 },
.cpu_arch = .thumb,
.cpu_features_add = std.Target.arm.featureSet(&[_]std.Target.arm.Feature{std.Target.arm.Feature.vfp4d16}),
.os_tag = .freestanding,
.abi = .none,
},
},
},
};
pub const Chip = struct {
name: []const u8,
manufacturer: []const u8,
cpu: Cpu,
memory_sections: []const MemorySection,
single_threaded: bool = false,
};
pub const Cpu = struct {
name: []const u8,
manufacturer: []const u8,
target: std.Target.Query,
};
pub const MemorySection = struct {
/// Origin of this memory region
origin: usize,
/// Size of this memory region
length: usize,
/// The type of memory this region is
type: Type,
const Type = union(enum) {
/// Non-volatile storage memory. Code goes here
FLASH,
/// Volatile memory. Variables go here.
RAM,
/// Memory region that exists, but must not be accessed.
RESERVED,
/// Board-specific memory region
CUSTOM: CustomRegion,
};
const CustomRegion = struct {
/// Custom regions do not have automatic name assignment, it must
/// be unique and provided by user. Compile error otherwise.
name: []const u8,
/// What can you do with this region?
permissions: Permissions,
const Permissions = packed struct(u3) {
/// Can you read from this region?
readable: bool,
/// Can you write to this region?
writable: bool,
/// Can you execute code from this region?
executable: bool,
};
};
};
/// Utility for handling different memory size units.
const Units = .{
// Linker uses B by default, so no conversion required.
.B = 1,
// Convert KB to B
.KB = 1024,
// Convert MB to B
.MB = 1024 * 1024,
};