A3: S12 Wafer Maps stream (E5 §13)
Adds the core map-management messages: setup send/request (F1-F4), transmit inquire/grant (F5/F6), data send in row format (F7/F8), and the one-way error report (F19). Reference points (REFP) are an external struct shared across F1 and F4. The alternative data encodings — array format (F9/F10), coordinate format (F11/F12), and the corresponding request pairs (F13-F18) — are scope-deferred. They're mechanical YAML edits once a tool needs them; the codec already handles the underlying BINARY/list shapes. Three new ack enums: MapSetupAck (SDACK), MapTransmitGrant (GRANT), MapDataAck (MAPER). No state machine yet — maps are a data exchange, not a lifecycle. secsgem-py ships S12F0-F19 as a single block; this commit covers the practically-used subset and matches their wire shapes where they overlap. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -176,6 +176,11 @@ YAML/handler extensions, not surgery):
|
|||||||
| S5F15 / S5F16 | E→H | ✅ | catalog | ✅ round-trip (exception recover complete) |
|
| S5F15 / S5F16 | E→H | ✅ | catalog | ✅ round-trip (exception recover complete) |
|
||||||
| S5F17 / S5F18 | H→E | ✅ | catalog | ✅ round-trip (exception recover abort) |
|
| S5F17 / S5F18 | H→E | ✅ | catalog | ✅ round-trip (exception recover abort) |
|
||||||
| S2F49 / S2F50 | H→E | ✅ | catalog + server | ✅ round-trip + dispatch (enhanced remote command, OBJSPEC + per-CP CPACK/CEPACK) |
|
| S2F49 / S2F50 | H→E | ✅ | catalog + server | ✅ round-trip + dispatch (enhanced remote command, OBJSPEC + per-CP CPACK/CEPACK) |
|
||||||
|
| S12F1 / S12F2 | H→E | ✅ | catalog | ✅ round-trip (Map Setup Data Send, E5 §13) |
|
||||||
|
| S12F3 / S12F4 | H→E | ✅ | catalog | ✅ round-trip (Map Setup Data Request) |
|
||||||
|
| S12F5 / S12F6 | E→H | ✅ | catalog | ✅ round-trip (Map Transmit Inquire / Grant) |
|
||||||
|
| S12F7 / S12F8 | E→H | ✅ | catalog | ✅ round-trip (Map Data Send, row format MAPFT=0) |
|
||||||
|
| S12F19 | E→H | ✅ | catalog | ✅ round-trip (Map Error Send, one-way) |
|
||||||
| S6F1 / S6F2 | E→H | ✅ | catalog | ✅ round-trip |
|
| S6F1 / S6F2 | E→H | ✅ | catalog | ✅ round-trip |
|
||||||
| S6F11 / S6F12 | E→H | ✅ | catalog | ✅ round-trip + demo |
|
| S6F11 / S6F12 | E→H | ✅ | catalog | ✅ round-trip + demo |
|
||||||
| S6F23 / S6F24 | H→E | ✅ | catalog | ✅ round-trip + demo |
|
| S6F23 / S6F24 | H→E | ✅ | catalog | ✅ round-trip + demo |
|
||||||
|
|||||||
@@ -1089,6 +1089,163 @@ messages:
|
|||||||
builder: s10f6_terminal_display_multi_ack
|
builder: s10f6_terminal_display_multi_ack
|
||||||
body: {kind: scalar, item_type: BINARY_BYTE, enum: TerminalAck, param: ack}
|
body: {kind: scalar, item_type: BINARY_BYTE, enum: TerminalAck, param: ack}
|
||||||
|
|
||||||
|
# =====================================================================
|
||||||
|
# S12 — Wafer Maps (E5 §13).
|
||||||
|
#
|
||||||
|
# IDTYP discriminates the kind of identifier carried by MID:
|
||||||
|
# 0 wafer ID, 1 substrate ID, 2 lot+slot, 3 design ID.
|
||||||
|
# MAPFT selects the data-payload format:
|
||||||
|
# 0 row format (F7/F8), 1 array format (F9/F10), 2 coordinate (F11/F12).
|
||||||
|
# Reference points are <L,2 RPX RPY> pairs (I4 coordinates).
|
||||||
|
#
|
||||||
|
# This first pass covers the setup + transmit-inquire + row-format data
|
||||||
|
# + error fan; the alternative-encoding pairs (F9/F10 array, F11/F12
|
||||||
|
# coord, F13-F18 corresponding requests) are scope-deferred — they're
|
||||||
|
# mechanical YAML edits once a tool needs them.
|
||||||
|
# =====================================================================
|
||||||
|
|
||||||
|
# S12F1 / F2 — Map Setup Data Send.
|
||||||
|
- id: S12F1
|
||||||
|
stream: 12
|
||||||
|
function: 1
|
||||||
|
w: true
|
||||||
|
builder: s12f1_map_setup_send
|
||||||
|
parser: parse_s12f1
|
||||||
|
body:
|
||||||
|
kind: list
|
||||||
|
struct_name: MapSetupData
|
||||||
|
fields:
|
||||||
|
- {name: mid, shape: {kind: scalar, item_type: ASCII}}
|
||||||
|
- {name: idtyp, shape: {kind: scalar, item_type: BINARY_BYTE}}
|
||||||
|
- {name: fnloc, shape: {kind: scalar, item_type: U2}}
|
||||||
|
- {name: ffrot, shape: {kind: scalar, item_type: U2}}
|
||||||
|
- {name: orloc, shape: {kind: scalar, item_type: BINARY_BYTE}}
|
||||||
|
- {name: rpsel, shape: {kind: scalar, item_type: U1}}
|
||||||
|
- name: refp
|
||||||
|
shape:
|
||||||
|
kind: list_of
|
||||||
|
element:
|
||||||
|
kind: list
|
||||||
|
struct_name: ReferencePoint
|
||||||
|
external_struct: true
|
||||||
|
fields:
|
||||||
|
- {name: x, shape: {kind: scalar, item_type: I4}}
|
||||||
|
- {name: y, shape: {kind: scalar, item_type: I4}}
|
||||||
|
- name: dutms
|
||||||
|
shape: {kind: list_of, element: {kind: scalar, item_type: ASCII}}
|
||||||
|
|
||||||
|
- id: S12F2
|
||||||
|
stream: 12
|
||||||
|
function: 2
|
||||||
|
builder: s12f2_map_setup_ack
|
||||||
|
body: {kind: scalar, item_type: BINARY_BYTE, enum: MapSetupAck, param: sdack}
|
||||||
|
|
||||||
|
# S12F3 / F4 — Map Setup Data Request.
|
||||||
|
- id: S12F3
|
||||||
|
stream: 12
|
||||||
|
function: 3
|
||||||
|
w: true
|
||||||
|
builder: s12f3_map_setup_request
|
||||||
|
parser: parse_s12f3
|
||||||
|
body:
|
||||||
|
kind: list
|
||||||
|
struct_name: MapSetupRequest
|
||||||
|
fields:
|
||||||
|
- {name: mid, shape: {kind: scalar, item_type: ASCII}}
|
||||||
|
- {name: idtyp, shape: {kind: scalar, item_type: BINARY_BYTE}}
|
||||||
|
- {name: mapft, shape: {kind: scalar, item_type: BINARY_BYTE}}
|
||||||
|
|
||||||
|
- id: S12F4
|
||||||
|
stream: 12
|
||||||
|
function: 4
|
||||||
|
builder: s12f4_map_setup_reply
|
||||||
|
parser: parse_s12f4
|
||||||
|
body:
|
||||||
|
kind: list
|
||||||
|
struct_name: MapSetupReply
|
||||||
|
fields:
|
||||||
|
- {name: mid, shape: {kind: scalar, item_type: ASCII}}
|
||||||
|
- {name: idtyp, shape: {kind: scalar, item_type: BINARY_BYTE}}
|
||||||
|
- {name: fnloc, shape: {kind: scalar, item_type: U2}}
|
||||||
|
- {name: orloc, shape: {kind: scalar, item_type: BINARY_BYTE}}
|
||||||
|
- {name: rpsel, shape: {kind: scalar, item_type: U1}}
|
||||||
|
- name: refp
|
||||||
|
shape:
|
||||||
|
kind: list_of
|
||||||
|
element:
|
||||||
|
kind: list
|
||||||
|
struct_name: ReferencePoint
|
||||||
|
external_struct: true
|
||||||
|
fields:
|
||||||
|
- {name: x, shape: {kind: scalar, item_type: I4}}
|
||||||
|
- {name: y, shape: {kind: scalar, item_type: I4}}
|
||||||
|
- name: dutms
|
||||||
|
shape: {kind: list_of, element: {kind: scalar, item_type: ASCII}}
|
||||||
|
- {name: xdies, shape: {kind: scalar, item_type: U2}}
|
||||||
|
- {name: ydies, shape: {kind: scalar, item_type: U2}}
|
||||||
|
|
||||||
|
# S12F5 / F6 — Map Transmit Inquire / Grant. Equipment asks the host
|
||||||
|
# for permission to send a map (MAPFT specifies the format, MLCL is
|
||||||
|
# the predicted data length in die units).
|
||||||
|
- id: S12F5
|
||||||
|
stream: 12
|
||||||
|
function: 5
|
||||||
|
w: true
|
||||||
|
builder: s12f5_map_transmit_inquire
|
||||||
|
parser: parse_s12f5
|
||||||
|
body:
|
||||||
|
kind: list
|
||||||
|
struct_name: MapTransmitInquire
|
||||||
|
fields:
|
||||||
|
- {name: mid, shape: {kind: scalar, item_type: ASCII}}
|
||||||
|
- {name: idtyp, shape: {kind: scalar, item_type: BINARY_BYTE}}
|
||||||
|
- {name: mapft, shape: {kind: scalar, item_type: BINARY_BYTE}}
|
||||||
|
- {name: mlcl, shape: {kind: scalar, item_type: U4}}
|
||||||
|
|
||||||
|
- id: S12F6
|
||||||
|
stream: 12
|
||||||
|
function: 6
|
||||||
|
builder: s12f6_map_transmit_grant
|
||||||
|
body: {kind: scalar, item_type: BINARY_BYTE, enum: MapTransmitGrant, param: grant}
|
||||||
|
|
||||||
|
# S12F7 / F8 — Map Data Send (row format, MAPFT=0). Each row of the
|
||||||
|
# map is one BINARY blob of packed bin codes; the outer list orders
|
||||||
|
# rows top-to-bottom relative to ORLOC.
|
||||||
|
- id: S12F7
|
||||||
|
stream: 12
|
||||||
|
function: 7
|
||||||
|
w: true
|
||||||
|
builder: s12f7_map_data_send_row
|
||||||
|
parser: parse_s12f7
|
||||||
|
body:
|
||||||
|
kind: list
|
||||||
|
struct_name: MapDataRow
|
||||||
|
fields:
|
||||||
|
- {name: mid, shape: {kind: scalar, item_type: ASCII}}
|
||||||
|
- {name: idtyp, shape: {kind: scalar, item_type: BINARY_BYTE}}
|
||||||
|
- name: rows
|
||||||
|
shape: {kind: list_of, element: {kind: scalar, item_type: BINARY}}
|
||||||
|
|
||||||
|
- id: S12F8
|
||||||
|
stream: 12
|
||||||
|
function: 8
|
||||||
|
builder: s12f8_map_data_send_row_ack
|
||||||
|
body: {kind: scalar, item_type: BINARY_BYTE, enum: MapDataAck, param: maper}
|
||||||
|
|
||||||
|
# S12F19 — Map Error Send. Equipment reports a non-recoverable map
|
||||||
|
# error (e.g. format mismatch, oversize payload). One-way (no reply).
|
||||||
|
- id: S12F19
|
||||||
|
stream: 12
|
||||||
|
function: 19
|
||||||
|
builder: s12f19_map_error_send
|
||||||
|
parser: parse_s12f19
|
||||||
|
body:
|
||||||
|
kind: list
|
||||||
|
struct_name: MapError
|
||||||
|
fields:
|
||||||
|
- {name: mapercd, shape: {kind: scalar, item_type: BINARY_BYTE}}
|
||||||
|
- {name: errtext, shape: {kind: scalar, item_type: ASCII}}
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# S14 — Object services (E94 ControlJob create/delete).
|
# S14 — Object services (E94 ControlJob create/delete).
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -140,4 +140,35 @@ enum class TerminalAck : uint8_t { // S10F2, S10F4
|
|||||||
TerminalNotAvailable = 2,
|
TerminalNotAvailable = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// E5 §13 S12 (Wafer Maps). Encoded as separate enums even though the
|
||||||
|
// underlying byte semantics differ slightly across F2/F6/F8 — keeping
|
||||||
|
// them distinct preserves call-site readability.
|
||||||
|
// Reference point on a wafer/substrate map (E5 §13.7.4 REFP). Declared
|
||||||
|
// out of line so the messages.yaml catalog can mark it `external_struct`
|
||||||
|
// and reuse the same C++ type across F1 / F4.
|
||||||
|
struct ReferencePoint {
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MapSetupAck : uint8_t { // S12F2 SDACK
|
||||||
|
Accept = 0,
|
||||||
|
Error = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MapTransmitGrant : uint8_t { // S12F6 GRANT
|
||||||
|
Granted = 0,
|
||||||
|
BusyTryAgain = 1,
|
||||||
|
NoSpaceAvailable = 2,
|
||||||
|
DuplicateMID = 3,
|
||||||
|
MaterialNotRecognized = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MapDataAck : uint8_t { // S12F8 MAPER
|
||||||
|
Accept = 0,
|
||||||
|
FormatError = 1,
|
||||||
|
UnknownID = 2,
|
||||||
|
AbortNoMap = 3,
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace secsgem::gem
|
} // namespace secsgem::gem
|
||||||
|
|||||||
@@ -657,3 +657,83 @@ TEST_CASE("S16F27 / S16F28 CJobCommand round-trip") {
|
|||||||
CHECK(parsed->ctljobcmd == "CJSTART");
|
CHECK(parsed->ctljobcmd == "CJSTART");
|
||||||
CHECK(*ack_byte(s16f28_cj_command_ack(HostCmdAck::Accept)) == 0);
|
CHECK(*ack_byte(s16f28_cj_command_ack(HostCmdAck::Accept)) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- S12 Wafer Maps (E5 §13) -------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("S12F1 / S12F2 Map Setup Data Send round-trip") {
|
||||||
|
std::vector<ReferencePoint> refp = {{0, 0}, {100, 200}};
|
||||||
|
std::vector<std::string> dutms = {"01", "02"};
|
||||||
|
auto m = s12f1_map_setup_send("WAFER-1", /*idtyp=*/0, /*fnloc=*/0,
|
||||||
|
/*ffrot=*/0, /*orloc=*/2, /*rpsel=*/2,
|
||||||
|
refp, dutms);
|
||||||
|
CHECK(m.stream == 12);
|
||||||
|
CHECK(m.function == 1);
|
||||||
|
CHECK(m.reply_expected);
|
||||||
|
|
||||||
|
auto parsed = parse_s12f1(m);
|
||||||
|
REQUIRE(parsed.has_value());
|
||||||
|
CHECK(parsed->mid == "WAFER-1");
|
||||||
|
CHECK(parsed->idtyp == 0);
|
||||||
|
CHECK(parsed->orloc == 2);
|
||||||
|
REQUIRE(parsed->refp.size() == 2);
|
||||||
|
CHECK(parsed->refp[1].x == 100);
|
||||||
|
CHECK(parsed->refp[1].y == 200);
|
||||||
|
REQUIRE(parsed->dutms.size() == 2);
|
||||||
|
CHECK(parsed->dutms[0] == "01");
|
||||||
|
|
||||||
|
CHECK(*ack_byte(s12f2_map_setup_ack(MapSetupAck::Accept)) == 0);
|
||||||
|
CHECK(*ack_byte(s12f2_map_setup_ack(MapSetupAck::Error)) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("S12F3 / S12F4 Map Setup Data Request round-trip") {
|
||||||
|
auto req = s12f3_map_setup_request("WAFER-1", /*idtyp=*/0, /*mapft=*/0);
|
||||||
|
auto parsed = parse_s12f3(req);
|
||||||
|
REQUIRE(parsed.has_value());
|
||||||
|
CHECK(parsed->mid == "WAFER-1");
|
||||||
|
CHECK(parsed->mapft == 0);
|
||||||
|
|
||||||
|
std::vector<ReferencePoint> refp = {{0, 0}};
|
||||||
|
auto reply = s12f4_map_setup_reply("WAFER-1", /*idtyp=*/0, /*fnloc=*/270,
|
||||||
|
/*orloc=*/0, /*rpsel=*/1, refp,
|
||||||
|
{"R1C1"}, /*xdies=*/4, /*ydies=*/4);
|
||||||
|
auto preply = parse_s12f4(reply);
|
||||||
|
REQUIRE(preply.has_value());
|
||||||
|
CHECK(preply->fnloc == 270);
|
||||||
|
CHECK(preply->xdies == 4);
|
||||||
|
CHECK(preply->ydies == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("S12F5 / S12F6 Map Transmit Inquire / Grant round-trip") {
|
||||||
|
auto m = s12f5_map_transmit_inquire("WAFER-1", /*idtyp=*/0,
|
||||||
|
/*mapft=*/0, /*mlcl=*/256);
|
||||||
|
auto parsed = parse_s12f5(m);
|
||||||
|
REQUIRE(parsed.has_value());
|
||||||
|
CHECK(parsed->mlcl == 256);
|
||||||
|
|
||||||
|
CHECK(*ack_byte(s12f6_map_transmit_grant(MapTransmitGrant::Granted)) == 0);
|
||||||
|
CHECK(*ack_byte(s12f6_map_transmit_grant(MapTransmitGrant::BusyTryAgain)) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("S12F7 / S12F8 Map Data Send (row format) round-trip") {
|
||||||
|
std::vector<std::string> rows = {
|
||||||
|
std::string({0x01, 0x02, 0x03}),
|
||||||
|
std::string({0x04, 0x05, 0x06}),
|
||||||
|
};
|
||||||
|
auto m = s12f7_map_data_send_row("WAFER-1", /*idtyp=*/0, rows);
|
||||||
|
auto parsed = parse_s12f7(m);
|
||||||
|
REQUIRE(parsed.has_value());
|
||||||
|
REQUIRE(parsed->rows.size() == 2);
|
||||||
|
CHECK(parsed->rows[0].size() == 3);
|
||||||
|
CHECK(static_cast<uint8_t>(parsed->rows[1][2]) == 0x06);
|
||||||
|
|
||||||
|
CHECK(*ack_byte(s12f8_map_data_send_row_ack(MapDataAck::Accept)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("S12F19 Map Error Send round-trip") {
|
||||||
|
auto m = s12f19_map_error_send(/*mapercd=*/2, "unrecognized MID");
|
||||||
|
auto parsed = parse_s12f19(m);
|
||||||
|
REQUIRE(parsed.has_value());
|
||||||
|
CHECK(parsed->mapercd == 2);
|
||||||
|
CHECK(parsed->errtext == "unrecognized MID");
|
||||||
|
CHECK_FALSE(m.reply_expected);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user