commit 2b0ab579ae6da0a3ae144bd364874ea967279eb8
parent 0ac79702203fef35f9b7f91eec72d3e418570a7e
Author: Brian Swetland <swetland@frotz.net>
Date:   Sun, 12 Jul 2015 04:48:41 -0700
agents: simplify things, decouple from the arch/chip system
Diffstat:
11 files changed, 397 insertions(+), 291 deletions(-)
diff --git a/Makefile b/Makefile
@@ -61,6 +61,7 @@ OUT_TARGET_OBJ := $(OUT)/_obj/target
 
 ALL :=
 DEPS :=
+AGENTS :=
 
 # build system internals
 include build/build.mk
diff --git a/agents/lpc-header.S b/agents/lpc-header.S
@@ -1,22 +0,0 @@
-
-.section .vectors
-.syntax unified
-.globl _start
-
-_start:
-	.long 0x42776166	// magic
-	.long 0x00010000	// version
-	.long 0x00000001	// flags
-	.long _start
-	.long _start + 0x400	// data addr
-	.long 0x00001000	// data size
-	.long CONFIG_FLASHADDR	// flash addr
-	.long CONFIG_FLASHSIZE	// flash size
-	.long 0
-	.long 0
-	.long 0
-	.long 0
-	.long flash_agent_setup + 1
-	.long flash_agent_erase + 1
-	.long flash_agent_write + 1
-	.long flash_agent_ioctl + 1
diff --git a/agents/lpc-main.c b/agents/lpc-main.c
@@ -1,106 +0,0 @@
-// agent-lpc15xx/main.c
-//
-// Copyright 2015 Brian Swetland <swetland@frotz.net>
-// 
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <agent/flash.h>
-
-#ifdef CONFIG_ARCH_LPC15XX
-#define LPC_IAP_FUNC	0x03000205
-#else
-#define LPC_IAP_FUNC	0x1fff1ff1
-#endif
-
-#define LPC_IAP_PREPARE	50
-#define LPC_IAP_WRITE	51
-#define LPC_IAP_ERASE	52
-
-// Note that while the databook claims you can reuse the same array
-// for both parameters and results, this is a lie.  Attempting to 
-// do so causes an invalid command failure.
-
-
-int flash_agent_setup(flash_agent *agent) {
-	return ERR_NONE;
-}
-
-int flash_agent_erase(u32 flash_addr, u32 length) {
-	void (*romcall)(u32 *, u32 *) = (void*) LPC_IAP_FUNC;
-	u32 p[5],r[4];
-	u32 page = flash_addr >> 12;
-	u32 last = page + ((length - 1) >> 12);
-	if (flash_addr & 0xFFF) {
-		return ERR_ALIGNMENT;
-	}
-
-	p[0] = LPC_IAP_PREPARE;
-	p[1] = page;
-	p[2] = last;
-	romcall(p,r);
-	if (r[0]) {
-		return ERR_FAIL;
-	}
-
-	p[0] = LPC_IAP_ERASE;
-	p[1] = page;
-	p[2] = last;
-	p[3] = 0x2ee0;
-	romcall(p,r);
-	if (r[0]) {
-		return ERR_FAIL;
-	}
-	return ERR_NONE;
-}
-
-int flash_agent_write(u32 flash_addr, const void *data, u32 length) {
-	void (*romcall)(u32 *,u32 *) = (void*) LPC_IAP_FUNC;
-	u32 p[5],r[4];
-	u32 page = flash_addr >> 12;
-	if (flash_addr & 0xFFF) {
-		return ERR_ALIGNMENT;
-	}
-
-	p[0] = LPC_IAP_PREPARE;
-	p[1] = page;
-	p[2] = page;
-	romcall(p,r);
-	if (r[0]) {
-		return ERR_FAIL;
-	}
-
-	// todo: smaller writes, etc
-	if (length != 4096) {
-		int n;
-		for (n = length; n < 4096; n++) {
-			((char*) data)[n] = 0;
-		}
-	}
-	p[0] = LPC_IAP_WRITE;
-	p[1] = flash_addr;
-	p[2] = (u32) data;
-	p[3] = 0x1000;
-	p[4] = 0x2ee0;
-	romcall(p,r);
-	if (r[0]) {
-		return ERR_FAIL;
-	}
-
-	return ERR_NONE;
-}
-
-int flash_agent_ioctl(u32 op, void *ptr, u32 arg0, u32 arg1) {
-	return ERR_INVALID;
-}
-
-
diff --git a/agents/lpc13xx_lpc15xx.c b/agents/lpc13xx_lpc15xx.c
@@ -0,0 +1,119 @@
+// agent-lpc15xx/main.c
+//
+// Copyright 2015 Brian Swetland <swetland@frotz.net>
+// 
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <agent/flash.h>
+
+#ifdef CONFIG_ARCH_LPC15XX
+#define LPC_IAP_FUNC	0x03000205
+#else
+#define LPC_IAP_FUNC	0x1fff1ff1
+#endif
+
+#define LPC_IAP_PREPARE	50
+#define LPC_IAP_WRITE	51
+#define LPC_IAP_ERASE	52
+
+// Note that while the databook claims you can reuse the same array
+// for both parameters and results, this is a lie.  Attempting to 
+// do so causes an invalid command failure.
+
+
+int flash_agent_setup(flash_agent *agent) {
+	return ERR_NONE;
+}
+
+int flash_agent_erase(u32 flash_addr, u32 length) {
+	void (*romcall)(u32 *, u32 *) = (void*) LPC_IAP_FUNC;
+	u32 p[5],r[4];
+	u32 page = flash_addr >> 12;
+	u32 last = page + ((length - 1) >> 12);
+	if (flash_addr & 0xFFF) {
+		return ERR_ALIGNMENT;
+	}
+
+	p[0] = LPC_IAP_PREPARE;
+	p[1] = page;
+	p[2] = last;
+	romcall(p,r);
+	if (r[0]) {
+		return ERR_FAIL;
+	}
+
+	p[0] = LPC_IAP_ERASE;
+	p[1] = page;
+	p[2] = last;
+	p[3] = 0x2ee0;
+	romcall(p,r);
+	if (r[0]) {
+		return ERR_FAIL;
+	}
+	return ERR_NONE;
+}
+
+int flash_agent_write(u32 flash_addr, const void *data, u32 length) {
+	void (*romcall)(u32 *,u32 *) = (void*) LPC_IAP_FUNC;
+	u32 p[5],r[4];
+	u32 page = flash_addr >> 12;
+	if (flash_addr & 0xFFF) {
+		return ERR_ALIGNMENT;
+	}
+
+	p[0] = LPC_IAP_PREPARE;
+	p[1] = page;
+	p[2] = page;
+	romcall(p,r);
+	if (r[0]) {
+		return ERR_FAIL;
+	}
+
+	// todo: smaller writes, etc
+	if (length != 4096) {
+		int n;
+		for (n = length; n < 4096; n++) {
+			((char*) data)[n] = 0;
+		}
+	}
+	p[0] = LPC_IAP_WRITE;
+	p[1] = flash_addr;
+	p[2] = (u32) data;
+	p[3] = 0x1000;
+	p[4] = 0x2ee0;
+	romcall(p,r);
+	if (r[0]) {
+		return ERR_FAIL;
+	}
+
+	return ERR_NONE;
+}
+
+int flash_agent_ioctl(u32 op, void *ptr, u32 arg0, u32 arg1) {
+	return ERR_INVALID;
+}
+
+const flash_agent __attribute((section(".vectors"))) FlashAgent = {
+	.magic =	AGENT_MAGIC,
+	.version =	AGENT_VERSION,
+	.flags =	FLAG_BOOT_ROM_HACK,
+	.load_addr =	CONFIG_LOADADDR,
+	.data_addr =	CONFIG_LOADADDR + 0x400,
+	.data_size =	0x1000,
+	.flash_addr =	CONFIG_FLASHADDR,
+	.flash_size =	CONFIG_FLASHSIZE,
+	.setup =	flash_agent_setup,
+	.erase =	flash_agent_erase,
+	.write =	flash_agent_write,
+	.ioctl =	flash_agent_ioctl,
+};
diff --git a/agents/module.mk b/agents/module.mk
@@ -1,18 +1,26 @@
 
 M_NAME := agent-lpc15xx
-M_CHIP := lpc1547-agt
-M_START := agents/lpc-header.o
-M_OBJS := agents/lpc-main.o
-$(call build-target-executable)
+M_CONFIG := ARCH_LPC15xx=1
+M_ROMBASE := 0x00000000
+M_ROMSIZE := 0x00010000
+M_RAMBASE := 0x02000400
+M_RAMSIZE := 0x00000400
+M_OBJS := agents/lpc13xx_lpc15xx.o
+$(call build-target-agent)
 
 M_NAME := agent-lpc13xx
-M_CHIP := lpc1343-agt
-M_START := agents/lpc-header.o
-M_OBJS := agents/lpc-main.o
-$(call build-target-executable)
+M_ROMBASE := 0x00000000
+M_ROMSIZE := 0x00008000
+M_RAMBASE := 0x10000400
+M_RAMSIZE := 0x00000400
+M_OBJS := agents/lpc13xx_lpc15xx.o
+$(call build-target-agent)
 
 M_NAME := agent-stm32f4xx
-M_CHIP := stm32f4xx-agt
-M_START := agents/stm-header.o
-M_OBJS := agents/stm-main.o
-$(call build-target-executable)
+M_ROMBASE := 0x00000000
+M_ROMSIZE := 0x00100000
+M_RAMBASE := 0x20000400
+M_RAMSIZE := 0x00000400
+M_OBJS := agents/stm32fxxx.o
+$(call build-target-agent)
+
diff --git a/agents/stm-header.S b/agents/stm-header.S
@@ -1,22 +0,0 @@
-
-.section .vectors
-.syntax unified
-.globl _start
-
-_start:
-	.long 0x42776166	// magic
-	.long 0x00010000	// version
-	.long 0x00000000	// flags
-	.long _start
-	.long _start + 0x400	// data addr
-	.long 0x00008000	// data size
-	.long CONFIG_FLASHADDR	// flash addr
-	.long CONFIG_FLASHSIZE	// flash size
-	.long 0
-	.long 0
-	.long 0
-	.long 0
-	.long flash_agent_setup + 1
-	.long flash_agent_erase + 1
-	.long flash_agent_write + 1
-	.long flash_agent_ioctl + 1
diff --git a/agents/stm-main.c b/agents/stm-main.c
@@ -1,128 +0,0 @@
-// agents/stm-main.c
-//
-// Copyright 2015 Brian Swetland <swetland@frotz.net>
-// 
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <agent/flash.h>
-#include <fw/io.h>
-
-#define FLASH_BASE		0x40023C00
-#define FLASH_ACR		(FLASH_BASE + 0x00)
-
-#define FLASH_KEYR		(FLASH_BASE + 0x04)
-#define FLASH_KEYR_KEY1		0x45670123
-#define FLASH_KEYR_KEY2		0xCDEF89AB
-
-#define FLASH_SR		(FLASH_BASE + 0x0C)
-#define FLASH_SR_BSY		(1 << 16)
-#define FLASH_SR_PGSERR		(1 << 7) // sequence error
-#define FLASH_SR_PGPERR		(1 << 6) // parallelism error
-#define FLASH_SR_PGAERR		(1 << 5) // alignment error
-#define FLASH_SR_WRPERR		(1 << 4) // write-protect error
-#define FLASH_SR_OPERR		(1 << 1) // operation error
-#define FLASH_SR_ERRMASK	0xF2
-#define FLASH_SR_EOP		(1 << 0) // end of operation
-
-#define FLASH_CR		(FLASH_BASE + 0x10)
-#define FLASH_CR_LOCK		(1 << 31)
-#define FLASH_CR_ERRIE		(1 << 25) // error irq en
-#define FLASH_CR_EOPIE		(1 << 24) // end of op irq en
-#define FLASH_CR_STRT		(1 << 16) // start
-#define FLASH_CR_PSIZE_8	(0 << 8)
-#define FLASH_CR_PSIZE_16	(1 << 8)
-#define FLASH_CR_PSIZE_32	(2 << 8)
-#define FLASH_CR_PSIZE_64	(3 << 8)
-#define FLASH_CR_SNB(n)		(((n) & 15) << 3) // sector number
-#define FLASH_CR_MER		(1 << 2) // mass erase
-#define FLASH_CR_SER		(1 << 1) // sector erase
-#define FLASH_CR_PG		(1 << 0) // programming
-
-
-#define SECTORS 12
-
-static u32 sectors[SECTORS + 1] = {
-	0x00000000,
-	0x00004000,
-	0x00008000,
-	0x0000C000,
-	0x00010000,
-	0x00020000,
-	0x00040000,
-	0x00060000,
-	0x00080000,
-	0x000A0000,
-	0x000C0000,
-	0x000E0000,
-	0x00100000,
-};
-
-int flash_agent_setup(flash_agent *agent) {
-	writel(FLASH_KEYR_KEY1, FLASH_KEYR);
-	writel(FLASH_KEYR_KEY2, FLASH_KEYR);
-	if (readl(FLASH_CR) & FLASH_CR_LOCK) {
-		return ERR_FAIL;
-	} else {
-		return ERR_NONE;
-	}
-}
-
-int flash_agent_erase(u32 flash_addr, u32 length) {
-	u32 v;
-	int n;
-	for (n = 0; n < SECTORS; n++) {
-		if (flash_addr == sectors[n]) goto ok;
-	}
-	return ERR_ALIGNMENT;
-ok:
-	for (;;) {
-		writel(FLASH_CR_SER | FLASH_CR_SNB(n), FLASH_CR);
-		writel(FLASH_CR_STRT | FLASH_CR_SER | FLASH_CR_SNB(n), FLASH_CR);
-		while ((v = readl(FLASH_SR)) & FLASH_SR_BSY) ;
-		if (v & FLASH_SR_ERRMASK) {
-			return ERR_FAIL;
-		}
-		n++;
-		if (n == SECTORS) break;
-		if ((sectors[n] - flash_addr) >= length) break;
-	}
-	return ERR_NONE;
-}
-
-int flash_agent_write(u32 flash_addr, const void *_data, u32 length) {
-	const u32 *data = _data;
-	u32 v;
-	if ((flash_addr & 3) || (length & 3)) {
-		return ERR_ALIGNMENT;
-	}
-	writel(FLASH_CR_PG | FLASH_CR_PSIZE_32, FLASH_CR);
-	while (length > 0) {
-		writel(*data, flash_addr);
-		data++;
-		length -= 4;
-		flash_addr += 4;
-		while ((v = readl(FLASH_SR)) & FLASH_SR_BSY) ;
-		if (v & FLASH_SR_ERRMASK) {
-			writel(0, FLASH_CR);
-			return ERR_FAIL;
-		}
-	}
-	writel(0, FLASH_CR);
-	return ERR_NONE;
-}
-
-int flash_agent_ioctl(u32 op, void *ptr, u32 arg0, u32 arg1) {
-	return ERR_INVALID;
-}
-
-
diff --git a/agents/stm32fxxx.c b/agents/stm32fxxx.c
@@ -0,0 +1,141 @@
+// agents/stm-main.c
+//
+// Copyright 2015 Brian Swetland <swetland@frotz.net>
+// 
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <agent/flash.h>
+#include <fw/io.h>
+
+#define FLASH_BASE		0x40023C00
+#define FLASH_ACR		(FLASH_BASE + 0x00)
+
+#define FLASH_KEYR		(FLASH_BASE + 0x04)
+#define FLASH_KEYR_KEY1		0x45670123
+#define FLASH_KEYR_KEY2		0xCDEF89AB
+
+#define FLASH_SR		(FLASH_BASE + 0x0C)
+#define FLASH_SR_BSY		(1 << 16)
+#define FLASH_SR_PGSERR		(1 << 7) // sequence error
+#define FLASH_SR_PGPERR		(1 << 6) // parallelism error
+#define FLASH_SR_PGAERR		(1 << 5) // alignment error
+#define FLASH_SR_WRPERR		(1 << 4) // write-protect error
+#define FLASH_SR_OPERR		(1 << 1) // operation error
+#define FLASH_SR_ERRMASK	0xF2
+#define FLASH_SR_EOP		(1 << 0) // end of operation
+
+#define FLASH_CR		(FLASH_BASE + 0x10)
+#define FLASH_CR_LOCK		(1 << 31)
+#define FLASH_CR_ERRIE		(1 << 25) // error irq en
+#define FLASH_CR_EOPIE		(1 << 24) // end of op irq en
+#define FLASH_CR_STRT		(1 << 16) // start
+#define FLASH_CR_PSIZE_8	(0 << 8)
+#define FLASH_CR_PSIZE_16	(1 << 8)
+#define FLASH_CR_PSIZE_32	(2 << 8)
+#define FLASH_CR_PSIZE_64	(3 << 8)
+#define FLASH_CR_SNB(n)		(((n) & 15) << 3) // sector number
+#define FLASH_CR_MER		(1 << 2) // mass erase
+#define FLASH_CR_SER		(1 << 1) // sector erase
+#define FLASH_CR_PG		(1 << 0) // programming
+
+
+#define SECTORS 12
+
+static u32 sectors[SECTORS + 1] = {
+	0x00000000,
+	0x00004000,
+	0x00008000,
+	0x0000C000,
+	0x00010000,
+	0x00020000,
+	0x00040000,
+	0x00060000,
+	0x00080000,
+	0x000A0000,
+	0x000C0000,
+	0x000E0000,
+	0x00100000,
+};
+
+int flash_agent_setup(flash_agent *agent) {
+	writel(FLASH_KEYR_KEY1, FLASH_KEYR);
+	writel(FLASH_KEYR_KEY2, FLASH_KEYR);
+	if (readl(FLASH_CR) & FLASH_CR_LOCK) {
+		return ERR_FAIL;
+	} else {
+		return ERR_NONE;
+	}
+}
+
+int flash_agent_erase(u32 flash_addr, u32 length) {
+	u32 v;
+	int n;
+	for (n = 0; n < SECTORS; n++) {
+		if (flash_addr == sectors[n]) goto ok;
+	}
+	return ERR_ALIGNMENT;
+ok:
+	for (;;) {
+		writel(FLASH_CR_SER | FLASH_CR_SNB(n), FLASH_CR);
+		writel(FLASH_CR_STRT | FLASH_CR_SER | FLASH_CR_SNB(n), FLASH_CR);
+		while ((v = readl(FLASH_SR)) & FLASH_SR_BSY) ;
+		if (v & FLASH_SR_ERRMASK) {
+			return ERR_FAIL;
+		}
+		n++;
+		if (n == SECTORS) break;
+		if ((sectors[n] - flash_addr) >= length) break;
+	}
+	return ERR_NONE;
+}
+
+int flash_agent_write(u32 flash_addr, const void *_data, u32 length) {
+	const u32 *data = _data;
+	u32 v;
+	if ((flash_addr & 3) || (length & 3)) {
+		return ERR_ALIGNMENT;
+	}
+	writel(FLASH_CR_PG | FLASH_CR_PSIZE_32, FLASH_CR);
+	while (length > 0) {
+		writel(*data, flash_addr);
+		data++;
+		length -= 4;
+		flash_addr += 4;
+		while ((v = readl(FLASH_SR)) & FLASH_SR_BSY) ;
+		if (v & FLASH_SR_ERRMASK) {
+			writel(0, FLASH_CR);
+			return ERR_FAIL;
+		}
+	}
+	writel(0, FLASH_CR);
+	return ERR_NONE;
+}
+
+int flash_agent_ioctl(u32 op, void *ptr, u32 arg0, u32 arg1) {
+	return ERR_INVALID;
+}
+
+const flash_agent __attribute((section(".vectors"))) FlashAgent = {
+	.magic =	AGENT_MAGIC,
+	.version =	AGENT_VERSION,
+	.flags =	0,
+	.load_addr =	CONFIG_LOADADDR,
+	.data_addr =	CONFIG_LOADADDR + 0x400,
+	.data_size =	0x8000,
+	.flash_addr =	CONFIG_FLASHADDR,
+	.flash_size =	CONFIG_FLASHSIZE,
+	.setup =	flash_agent_setup,
+	.erase =	flash_agent_erase,
+	.write =	flash_agent_write,
+	.ioctl =	flash_agent_ioctl,
+};
diff --git a/build/build.mk b/build/build.mk
@@ -52,6 +52,7 @@ mv $1.tmp $1
 endef
 
 start-module-mk = $(eval M_MAKEFILE := $(lastword $(MAKEFILE_LIST)))
+build-target-agent = $(eval include build/target-agent.mk)
 build-target-executable = $(eval include build/target-executable.mk)
 build-host-executable = $(eval include build/host-executable.mk)
 
diff --git a/build/target-agent.mk b/build/target-agent.mk
@@ -0,0 +1,115 @@
+## Copyright 2011 Brian Swetland <swetland@frotz.net>
+## 
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+M_NAME := $(strip $(M_NAME))
+M_CHIP := $(strip $(M_CHIP))
+
+ifeq ($(strip $(M_START)),)
+M_START := $(ARCH_$(M_ARCH)_START) 
+endif
+
+M_ARCH := $(CHIP_$(M_CHIP)_ARCH)
+M_ARCH_CFLAGS := $(ARCH_$(M_ARCH)_CFLAGS)
+M_ARCH_OBJS := $(ARCH_$(M_ARCH)_OBJS)
+
+# sanity check
+ifeq "$(M_NAME)" ""
+$(error $(M_MAKEFILE): No module name specified)
+endif
+
+# architecture start glue goes first
+M_OBJS := $(addprefix $(OUT_TARGET_OBJ)/$(M_NAME)/,$(M_OBJS))
+
+DEPS += $(M_OBJS:%o=%d)
+
+M_OUT_BIN := $(OUT)/$(M_NAME).bin
+M_OUT_LST := $(OUT)/$(M_NAME).lst
+M_OUT_ELF := $(OUT)/$(M_NAME).elf
+
+ALL += $(M_OUT_BIN) $(M_OUT_LST) $(M_OUT_ELF)
+
+M_INCLUDE := $(OUT_TARGET_OBJ)/$(M_NAME)/include
+M_CONFIG_H := $(M_INCLUDE)/config.h
+M_LINK_SCRIPT := $(OUT_TARGET_OBJ)/$(M_NAME)/script.ld
+
+# generate link script 
+$(M_LINK_SCRIPT): _RADDR := $(M_RAMBASE)
+$(M_LINK_SCRIPT): _RSIZE := $(M_RAMSIZE)
+$(M_LINK_SCRIPT): $(CHIP_$(M_CHIP)_DEPS)
+	@echo linkscript $@
+	@echo "MEMORY {" > $@
+	@echo " RAM (xrw) : ORIGIN = $(_RADDR), LENGTH = $(_RSIZE)" >> $@
+	@echo "}" >> $@
+	@echo " INCLUDE \"build/generic-ram.ld\"" >> $@
+
+$(OUT_TARGET_OBJ)/$(M_NAME)/%.o: %.c
+	@$(MKDIR)
+	@echo compile $<
+	$(QUIET)$(TARGET_CC) $(TARGET_CFLAGS) $(_CFLAGS) -c $< -o $@ -MD -MT $@ -MF $(@:%o=%d)
+
+$(OUT_TARGET_OBJ)/$(M_NAME)/%.o: %.S
+	@$(MKDIR)
+	@echo assemble $<
+	$(QUIET)$(TARGET_CC) $(TARGET_CFLAGS) $(_CFLAGS) -c $< -o $@ -MD -MT $@ -MF $(@:%o=%d)
+
+# apply our flags to our objects
+$(M_OBJS): _CFLAGS := --include $(M_CONFIG_H) $(M_CFLAGS)
+$(M_ARCH_OBJS): _CFLAGS := --include $(M_CONFIG_H) $(M_CFLAGS)
+
+# objects depend on generated config header
+$(M_OBJS): $(M_CONFIG_H)
+$(M_ARCH_OBJS): $(M_CONFIG_H)
+
+X_CONFIG := FLASHSIZE=$(M_ROMSIZE)
+X_CONFIG += FLASHADDR=$(M_ROMBASE)
+X_CONFIG += LOADADDR=$(M_RAMBASE)
+
+# generate config header from module, chip, and arch config lists
+# generated config header depends on toplevel, module, and chip/arch makefiles
+$(M_CONFIG_H): _CFG := $(M_CONFIG) $(X_CONFIG)
+$(M_CONFIG_H): Makefile $(M_MAKEFILE)
+	@$(MKDIR)
+	@echo generate $@
+	@$(call make-config-header,$@,$(_CFG))
+
+$(M_OUT_BIN): $(M_OUT_ELF)
+	@echo create $@
+	$(QUIET)$(TARGET_OBJCOPY) --gap-fill=0xee -O binary $< $@
+
+$(M_OUT_LST): $(M_OUT_ELF)
+	@echo create $@
+	$(QUIET)$(TARGET_OBJDUMP) --source -d $< > $@
+
+$(M_OUT_ELF): _OBJS := $(M_OBJS)
+$(M_OUT_ELF): _LINK := $(M_LINK_SCRIPT)
+$(M_OUT_ELF): $(M_OBJS) $(M_LINK_SCRIPT)
+	@echo link $@
+	$(QUIET)$(TARGET_LD) $(TARGET_LFLAGS) -Bstatic -T $(_LINK) $(_OBJS) $(_LIBS) -o $@
+
+AGENTS += $(M_OUT_BIN)
+
+$(info module $(M_NAME))
+
+M_START :=
+M_OBJS :=
+M_NAME :=
+M_BASE :=
+M_LIBS :=
+M_CFLAGS :=
+M_CONFIG :=
+M_SIGN :=
+M_RAMBASE :=
+M_RAMSIZE :=
+M_ROMBASE :=
+M_ROMSIZE :=
diff --git a/tools/module.mk b/tools/module.mk
@@ -13,7 +13,6 @@ M_OBJS += tools/lkdebug.o
 M_OBJS += out/debugger-builtins.o
 $(call build-host-executable)
 
-AGENTS := out/agent-lpc13xx.bin out/agent-lpc15xx.bin out/agent-stm32f4xx.bin
 out/debugger-builtins.c: $(AGENTS) bin/mkbuiltins
 	@mkdir -p out
 	./bin/mkbuiltins $(AGENTS) > $@