This page is a walkthrough for a novel exploit for Hollywood, the ultimate challenge from the Microcorruption wargame originally developed by Matasano.
Fifty thousand people tried this;
five hundred succeeded.
Ten wrote about it: five brute-forced it in a higher level language,
Alex van Poppelen
Vítězslav Čížek
Zijun Hui
Ichiji001
Tux3
three used z3,
Guillaume Vigeant
Grazfather
Laanwj
and one broke the algorithm eighteen ways on an FPGA.
Aaron Ferrucci
Here's how to crack it in five lines of math on the back of a napkin.
There is a known issue with Android lacking a true monotype system font, which breaks many of the extended ASCII character set diagrams below. Please view this page on a Chrome or Firefox-based desktop browser to avoid rendering issues. Ideally on a Linux host.
The emulated device runs on the MSP430 instruction set architecture. It uses a 16-bit little-endian processor and has 64 kilobytes of RAM. The official manual includes the details, but relevant functionality is summarized below.
Several separate windows control the debugger functionality.
A user input prompt like the following is the device's external communication interface.
The equivalent of popping a shell on this system is calling interrupt 0x7F.
3240 00ff mov #0xff00, sr
b012 1000 call #0x10
324000ffb0121000
On this firmware version, there is a dedicated function that does this. The following message is displayed in the interface when the implementation calls this interrupt successfully.
The Hollywood firmware includes the following countermeasures.
Other walkthroughs explain how to circumvent these countermeasures in greater detail. Those wishing for a more complete analysis should consult the one by Grazfather.
The problem posed by the code jumping into the middle of multi-word instructions is relatively simple to circumvent with a program like Ghidra or IDA Pro, which are intelligent enough to follow the actual control flow and avoid instruction mangling. This tooling provides little information because it later chokes on the runtime packing.
The first objective is to break the random number generator so that it always adds zero to the base address. The following debugger macro patches the rand interrupt calls so they always return zero.
let 44c2=430f; let 44c4=4302; let 456e=430f; let 4570=4302
After single-stepping and manually reading a few thousand opcodes, it becomes apparent that there are intermittent calls to address 0xe000. On each call, that address will contain a single unique instruction that will execute before returning.
Again, this is after breaking the ASLR by patching out the rand calls. If the rand calls are left unpatched, the implementation will generate a random number, add it to the base address 0xe000, copy one instruction to the resulting randomized address, append a RET instruction, call the gadget, and zero it afterward. This cycle will then repeat. Another author, Guillaume Vigeant, referred to this behavior as a "poor man's virtual machine".
The "virtual machine" instructions can be single-stepped by breaking at address 0xe000, continuing, and observing the contents of the PC register with each iteration. The string below prints to the console, one character at a time, with every two steps.
What's the password?
The state of the system is only interesting after it reads untrusted input. Skipping to the gets interrupt call requires unbreaking address 0xe000 and continuing, which causes execution to skip to the prompt for user input. The macro to achieve this, starting from a reset, is as follows.
let 44c2=430f; let 44c4=4302; let 456e=430f; let 4570=4302; c; break e000
Stepping through VM execution from this point onward allows manual extraction of the following algorithm, instruction by instruction.
mov #0x2600, r5
clr r6
add @r5, r4
swpb r4
xor @r5+, r6
xor r4, r6
xor r6, r4
xor r4, r6
tst 0x0(r5)
mov sr, r7
and #0x2, r7
rra r7
xor #0x1, r7
swpb r7
rra r7
sxt r7
swpb r7
sxt r7
mov #0x4b18, r8
and r7, r8
xor #-0x1, r7
and #0x47aa, r7
add r7, r8
clr r7
add @r5, r4
swpb r4
xor @r5+, r6
xor r4, r6
xor r6, r4
xor r4, r6
tst 0x0(r5)
mov sr, r7
and #0x2, r7
rra r7
xor #0x1, r7
swpb r7
rra r7
mov #0x4b18, r8
and r7, r8
xor #-0x1, r7
and #0x47aa, r7
add r7, r8
clr r7
cmp #0xfeb1, r4
mov sr, r7
clr r4
cmp #0x9298, r6
and sr, r7
clr r6
rra r7
xor #0x1, r7
swpb r7
rra r7
rra r7
rra r7
rra r7
bis r7, sr
This code superficially resembles the bytecode emitted by a JIT compiler—a higher-level abstraction may be generating it at runtime.
The following checks occur near the end of the algorithm.
cmp #0xfeb1, r4
mov sr, r7
clr r4
cmp #0x9298, r6
and sr, r7
clr r6
These instructions set the contents of R7 based on the boolean result of the CMP instructions. After some massaging, this value is XORed with SR.
rra r7
xor #0x1, r7
swpb r7
rra r7
rra r7
rra r7
rra r7
bis r7, sr
For context, some bits in SR are hardware control flags.
See Figure 3-6 in this document.
If the previous comparisons fail, the code sets the "CPU OFF" bit in SR via a convoluted series of bitwise operations, halting the processor.
If they succeed, that bit remains unset, and the firmware calls the unlock interrupt.
The extracted algorithm comes from an execution trace, which does not include the unlock interrupt call because that instruction only executes when the password is valid.
The goal is simple: craft an input such that registers R4 and R6 contain 0xfeb1 and 0x9298, respectively.
This algorithm iterates over the supplied password, one 16-bit word at a time, to calculate a four-byte hash. The diagram below depicts the relevant sections of the algorithm.
Only operations that affect the contents of R4 and R6 are relevant. The diagram omits the rest of the instructions for brevity.
┌─────────┐ ┌────────────────────┐
│ INITIAL │ │mov #0x2600, r5│
│ SETUP │ │clr r6 │
└─────────┘ └────────────────────┘
┌─────────┐ ┌────────────────────┐◄──────┐
│ │ │add @r5, r4 │ │
│ │ │swpb r4 │ │
│HASHING │ │xor @r5+, r6 │ │
│ALGORITHM│ │xor r4, r6 │ "JNZ" │
│ │ │xor r6, r4 │ │
│ │ │xor r4, r6 │ │
│ │ │tst 0x0(r5) │ │
└─────────┘ └────────────────────┴───────┘
┌─────────┐ ┌────────────────────┐
│ │ │cmp #0xfeb1, r4│
│ │ │mov sr, r7 │
│ │ │clr r4 │
│ │ │cmp #0x9298, r6│
│ │ │and sr, r7 │
│ CHECK │ │clr r6 │
│ OUTPUT │ │rra r7 │
│ VALID │ │xor #0x1, r7 │
│ │ │swpb r7 │
│ │ │rra r7 │
│ │ │rra r7 │
│ │ │rra r7 │
│ │ │rra r7 │
│ │ │bis r7, sr │
└─────────┘ └────────────────────┘
The algorithm breaks out of the loop when the current word is null. It is possible to have null bytes in the password, but null words are prohibited.
Consider the highlighted section below.
┌─────────┐ ┌────────────────────┐◄──────┐
│ │ │add @r5, r4 │ │
│ │ │swpb r4 │ │
│HASHING │ │xor @r5+, r6 │ │
│ALGORITHM│ │xor r4, r6 │ "JNZ" │
│ │ │xor r6, r4 │ │
│ │ │xor r4, r6 │ │
│ │ │tst 0x0(r5) │ │
└─────────┘ └────────────────────┴───────┘
As other writeups have observed, this effectively swaps the contents of R4 and R6. The x86 platform typically implements such an operation with a single XCHG instruction. While the following assembly is not valid for the MSP430 ISA, the visual tweak makes the algorithm simpler to understand.
┌─────────┐ ┌────────────────────┐◄──────┐
│ │ │add @r5, r4 │ │
│HASHING │ │swpb r4 │ │
│ALGORITHM│ │xor @r5+, r6 │ "JNZ" │
│ │ │xchg r4, r6 │ │
│ │ │tst 0x0(r5) │ │
└─────────┘ └────────────────────┴───────┘
Consider what happens with the following test password. Note that all inputs are hex strings.
11223344
┌─────┐ ┌─────┐
│ R4 │ │ R6 │
├──┬──┤ ├──┬──┤
│00│00│ │00│00│
└──┴──┘ └──┴──┘
┌──────┬──┬──┬──┬──┬──┬──┐
│INPUT │11│22│33│44│00│00│
└──────┴──┴──┴──┴──┴──┴──┘
▲
│
┌┴───┐
│ R5 │
└────┘
┌──┐ ┌────────────────────┐
│PC├─► │add @r5, r4 │
└──┘ │swpb r4 │
│xor @r5+, r6 │
│xchg r4, r6 │
│tst 0x0(r5) │
└────────────────────┘
┌─────┐ ┌─────┐
│ R4 │ │ R6 │
├──┬──┤ ├──┬──┤
│22│11│ │00│00│
└──┴──┘ └──┴──┘
▲ ▲
│ │
│ ADD │
│ │
│ │
┌──────┼──┬──┼──┬──┬──┬──┐
│INPUT │11│22│33│44│00│00│
└──────┴──┴──┴──┴──┴──┴──┘
▲
│
┌┴───┐
│ R5 │
└────┘
┌────────────────────┐
┌──┐ │add @r5, r4 │
│PC├─► │swpb r4 │
└──┘ │xor @r5+, r6 │
│xchg r4, r6 │
│tst 0x0(r5) │
└────────────────────┘
┌─────┐ ┌─────┐
│ R4 │ │ R6 │
├──┬──┤ ├──┬──┤
│11│22│ │00│00│
└──┴──┘ └──┴──┘
◄────
SWPB
────►
┌──────┬──┬──┬──┬──┬──┬──┐
│INPUT │11│22│33│44│00│00│
└──────┴──┴──┴──┴──┴──┴──┘
▲
│
┌┴───┐
│ R5 │
└────┘
┌────────────────────┐
│add @r5, r4 │
┌──┐ │swpb r4 │
│PC├─► │xor @r5+, r6 │
└──┘ │xchg r4, r6 │
│tst 0x0(r5) │
└────────────────────┘
┌─────┐ ┌─────┐
│ R4 │ │ R6 │
├──┬──┤ ├──┬──┤
│11│22│ │22│11│
└──┴──┘ └──┴──┘
▲ ▲
┌────────────┘ │
│ XOR │
│ ┌────────────┘
│ │
┌──────┼──┬──┼──┬──┬──┬──┐
│INPUT │11│22│33│44│00│00│
└──────┴──┴──┴──┴──┴──┴──┘
▲
│
┌┴───┐
│ R5 │
└────┘
┌────────────────────┐
│add @r5, r4 │
│swpb r4 │
┌──┐ │xor @r5+, r6 │
│PC├─► │xchg r4, r6 │
└──┘ │tst 0x0(r5) │
└────────────────────┘
┌─────┐ ┌─────┐
│ R4 │ │ R6 │
├─────┤◄─────┼─────┤
│22 11│ XCHG │11 22│
└─────┴─────►└─────┘
┌──────┬──┬──┬──┬──┬──┬──┐
│INPUT │11│22│33│44│00│00│
└──────┴──┴──┴──┴──┴──┴──┘
▲
│
┌┴───┐
│ R5 │
└────┘
┌────────────────────┐
│add @r5, r4 │
│swpb r4 │
│xor @r5+, r6 │
┌──┐ │xchg r4, r6 │
│PC├─► │tst 0x0(r5) │
└──┘ └────────────────────┘
┌─────┐ ┌─────┐
│ R4 │ │ R6 │
├──┬──┤ ├──┬──┤
│22│11│ │11│22│
└──┴──┘ └──┴──┘
┌──────┬──┬──┬──┬──┐
│INPUT │33│44│00│00│
└──────┴──┴──┴──┴──┘
▲
│
┌┴───┐
│ R5 │
└────┘
┌──┐ ┌────────────────────┐
│PC├─► │add @r5, r4 │
└──┘ │swpb r4 │
│xor @r5+, r6 │
│xchg r4, r6 │
│tst 0x0(r5) │
└────────────────────┘
┌─────┐ ┌─────┐
│ R4 │ │ R6 │
├──┬──┤ ├──┬──┤
│66│44│ │11│22│
└──┴──┘ └──┴──┘
▲ ▲
│ │
│ ADD │
│ │
│ │
┌──────┼──┬──┼──┬──┐
│INPUT │33│44│00│00│
└──────┴──┴──┴──┴──┘
▲
│
┌┴───┐
│ R5 │
└────┘
┌────────────────────┐
┌──┐ │add @r5, r4 │
│PC├─► │swpb r4 │
└──┘ │xor @r5+, r6 │
│xchg r4, r6 │
│tst 0x0(r5) │
└────────────────────┘
┌─────┐ ┌─────┐
│ R4 │ │ R6 │
├──┬──┤ ├──┬──┤
│44│66│ │11│22│
└──┴──┘ └──┴──┘
◄────
SWPB
────►
┌──────┬──┬──┬──┬──┐
│INPUT │33│44│00│00│
└──────┴──┴──┴──┴──┘
▲
│
┌┴───┐
│ R5 │
└────┘
┌────────────────────┐
│add @r5, r4 │
┌──┐ │swpb r4 │
│PC├─► │xor @r5+, r6 │
└──┘ │xchg r4, r6 │
│tst 0x0(r5) │
└────────────────────┘
┌─────┐ ┌─────┐
│ R4 │ │ R6 │
├──┬──┤ ├──┬──┤
│44│66│ │55│11│
└──┴──┘ └──┴──┘
▲ ▲
┌────────────┘ │
│ XOR │
│ ┌────────────┘
│ │
┌──────┼──┬──┼──┬──┐
│INPUT │33│44│00│00│
└──────┴──┴──┴──┴──┘
▲
│
┌┴───┐
│ R5 │
└────┘
┌────────────────────┐
│add @r5, r4 │
│swpb r4 │
┌──┐ │xor @r5+, r6 │
│PC├─► │xchg r4, r6 │
└──┘ │tst 0x0(r5) │
└────────────────────┘
┌─────┐ ┌─────┐
│ R4 │ │ R6 │
├─────┤◄─────┼─────┤
│55 11│ XCHG │44 66│
└─────┴─────►└─────┘
┌──────┬──┬──┬──┬──┐
│INPUT │33│44│00│00│
└──────┴──┴──┴──┴──┘
▲
│
┌┴───┐
│ R5 │
└────┘
┌────────────────────┐
│add @r5, r4 │
│swpb r4 │
│xor @r5+, r6 │
┌──┐ │xchg r4, r6 │
│PC├─► │tst 0x0(r5) │
└──┘ └────────────────────┘
┌─────┐ ┌─────┐
│ R4 │ │ R6 │
├──┬──┤ ├──┬──┤
│55│11│ │44│66│
└──┴──┘ └──┴──┘
┌──────┬──┬──┐
│INPUT │00│00│
└──────┴──┴──┘
▲
│
┌┴───┐
│ R5 │
└────┘
┌────────────────────┐
│add @r5, r4 │
│swpb r4 │
│xor @r5+, r6 │
│xchg r4, r6 │
│tst 0x0(r5) │
└────────────────────┘
Most people with working exploits reimplement the algorithm in a higher-level language and brute-force the password (although some choose to use z3). Then, there is this man:
I concluded that I don't know the required math for solving this sort of puzzle. [...] Adding to my shame at not being able to simply solve for the password with math: the brute-force search for 5-byte passwords takes about 12 hours to run on my pretty-modern laptop. Then I thought of a way to salvage my dignity: I know how to design digital logic; I have access to programmable logic hardware; why not build a custom computing machine to find the passwords?
— Aaron Ferrucci
This FPGA implementation can brute-force all possible five-byte preimages in the time it takes to make coffee. Congratulations to Mr. Ferrucci for becoming a gold medalist in the resume-padding Olympics.
$ cloc hollywood_fpga_hash/
29 text files.
28 unique files.
6 files ignored.
github.com/AlDanial/cloc v 1.86 T=0.03 s (754.2 files/s, 232788.2 lines/s)
---------------------------------------------------------------------
Language files blank comment code
---------------------------------------------------------------------
XML 3 0 0 5094
Tcl/Tk 5 108 302 744
Verilog-SystemVerilog 7 74 18 372
C 2 39 7 288
Markdown 1 28 0 188
Stata 2 0 0 82
make 2 9 0 37
Mathematica 1 3 0 12
Bourne Shell 1 0 1 2
---------------------------------------------------------------------
SUM: 24 261 328 6819
---------------------------------------------------------------------
Owning this system does not require brute-forcing, SAT solvers, or FPGAs. That said, one must not begrudge those honor-bound to reimplement homebrew hashing algorithms in several hundred lines of SystemVerilog to avoid obligatory seppuku (as is required in the blood oath taken by all computer engineering graduates). Here is how to break the algorithm with a preimage attack in five lines of math. Why do this? Because yours truly does not like writing C. Cue the music.
The following debugger macros were helpful throughout the process of developing this technique.
#define vm-reset reset; unbreak e000
#define vm-init let 44c2=430f; let 44c4=4302; let 456e=430f; let 4570=4302; c; break e000
#define vm-init-hash c; c; c
#define vm-iter-hash c; c; c; c; c; c; c; c; c; c; c; c; c; c; c; c; c; c; c; c; c; c
Invoke the first two macros to set up the environment.
vm-reset
vm-init
Next, the firmware will read the password. After that, the following macro steps until the beginning of the first iteration of the hash loop.
vm-init-hash
At that point, the last command steps one complete hash iteration at a time.
vm-iter-hash
All values are 16-bit integers. Let the ordered pair represent the target image. Let be a hash function, and let
If and , solve for .
Instead of a preimage attack, suppose the goal was to perform a second preimage attack. That process requires finding some value with the following characteristics.
The only 16-bit value that satisfies these constraints is 0x8000.
Let the operator represent a 16-bit little-endian integer concatenation.
E.g., is "0300 0700".
For any arbitrary value ,
By way of example, the following two inputs produce the same hash.
ffff ffff
ffff 0080 0080 0080 0080 ffff
Let be a function that reverses the endianness of a 16-bit number. Given any arbitrary image on a hypothetical version of the implementation where null words are allowed,
b45d 0000 0000 0000 b45d ffff 4e01
On the real implementation, given and ,
b35d 0100 0100 0100 0180 0080 0080 0080 b55d ffff 4e01
If you were not connected to the debug lock, the door would now be open.
Try running "solve" in the debug console to see if this solution works without the debugger attached.
The CPU completed in 9979491 cycles.
Technically, this attack only requires four lines of math. The approach documented above is a curated version that is intended to be easier to understand. The original version, as implemented in July 2020, did not require the second preimage attack and only utilized the first four equations. An earlier revision of this page claimed that breaking the algorithm "does not require math". After digging up and reviewing some four-year-old notes, it is fairer to say that it does not require much math.
The original solution involved manual tinkering and a degree of intuition, so it is not straightforward to formally describe the process of going from the simpler theoretical implementation,
to the practical implementation. Commonalities are highlighted.
b252 0100 0100 0100 0005 0100 0001 0100 0004 0001 0100 0700 b55d ffff 4e01
Astute readers will notice certain structural similarities.
b35d 0100 0100 0100 0180 0080 0080 0080 b55d ffff 4e01
b252 0100 0100 0100 0005 0100 0001 0100 0004 0001 0100 0700 b55d ffff 4e01
The manual trial-and-error process required to create the latter payload is not worth discussing here; suffice it to say that it is possible.
$ cat hollywood_hash_core.sv
`default_nettype none
`timescale 1ns / 1ns
module hollywood_unhash_core #(
parameter R4 = 16'hFEB1,
parameter R6 = 16'h9298
)
(
input wire in_valid,
input wire in_channel,
input wire [15:0] in_data,
output reg out_valid,
output reg out_data,
input wire clk,
input wire reset
);
reg [15:0] r4, r6;
wire [15:0] swapped = {in_data[7:0], in_data[15:8]};
wire [15:0] next_r4, preswap_next_r6, next_r6;
assign next_r4 = r6 ^ swapped;
assign preswap_next_r6 = r4 + swapped;
assign next_r6 = {preswap_next_r6[7:0], preswap_next_r6[15:8]};
always @(posedge clk or posedge reset) begin
if (reset) begin
r4 <= '0;
r6 <= '0;
end
else begin
if (in_valid) begin
if (in_channel) begin
r4 <= '0;
r6 <= '0;
end
else begin
r4 <= next_r4;
r6 <= next_r6;
end
end
end
end
always @(posedge clk or posedge reset) begin
if (reset) begin
out_valid <= '0;
end
else begin
out_valid <= (r6 == R6) && (r4 == R4);
end
end
always @(posedge clk or posedge reset) begin
if (reset) begin
out_data <= '0;
end
else begin
out_data <= '0;
end
end
endmodule
`default_nettype wire
$ cat hollywood_hash_top.sv
`default_nettype none
`timescale 1ns / 1ns
module hollywood_hash_top(
output wire [4:0] LED,
input wire clk_50,
input wire global_reset_n
);
wire pll_locked;
wire [4:0] pio_val;
hollywood_hash hasher (
.reset_bridge_0_in_reset_reset_n (global_reset_n),
.clk_50_in_clk (clk_50),
.clk_adc_out_clk (),
.leds_export (pio_val),
.pll_c0_conduit_export (),
.pll_c1_conduit_export (),
.pll_c4_conduit_export (),
.pll_areset_conduit_export (1'b0),
.pll_locked_conduit_export (pll_locked)
);
assign LED = ~pio_val;
endmodule
`default_nettype wire
$ cat pw_gen.sv
This is one of the rare sports where saying "mine is smaller than yours" is bragging.
`default_nettype none
`timescale 1ns / 1ns
module pw_gen #(
parameter
PW1_RESET = 16'b0,
PW2_RESET = 16'b0,
PW3_RESET = 8'b0,
PW3_FINAL = 8'hFF) (
input wire in_write,
input wire in_read,
input wire in_addr,
input wire [31:0] in_writedata,
output reg [31:0] in_readdata,
output reg out_valid,
output reg out_channel,
output reg [15:0] out_data,
input wire req_valid,
input wire req_data,
output reg store_write,
output reg [5:0] store_addr,
output reg [15:0] store_writedata,
input wire store_waitrequest,
input wire clk,
input wire reset
);
reg [15:0] pw1;
reg [15:0] pw2;
reg [7:0] pw3;
typedef enum {
ST_IDLE,
ST_MGMT,
ST1,
ST2,
ST3
} t_state;
t_state state, p1_state;
reg p1_channel;
reg p1_valid;
reg [15:0] p1_data;
reg [15:0] p_pw1;
reg [15:0] p_pw2;
reg [7:0] p_pw3;
reg run;
reg pw1_overflow, pw2_overflow, pw3_overflow;
always_comb begin : state_transition
p1_state = ST_IDLE;
p1_channel = '0;
p1_data = '0;
p1_valid = '0;
p_pw1 = pw1;
p_pw2 = pw2;
p_pw3 = pw3;
case (state)
ST_IDLE: begin
if (run) begin
p1_state = ST1;
p1_channel = '0;
p_pw1 = pw1 + 1'b1;
p1_valid = '1;
end
else begin
p_pw1 = PW1_RESET[15:0];
p_pw2 = PW2_RESET[15:0];
p_pw3 = PW3_RESET[7:0];
end
end
ST1: begin
p1_state = ST2;
p1_channel = '0;
p1_valid = '1;
p1_data = pw2;
if (pw1_overflow)
p_pw2 = pw2 + 1'b1;
end
ST2: begin
p1_state = ST3;
p1_channel = '0;
p1_valid = '1;
p1_data = {pw3, 8'b0};
if (pw2_overflow)
p_pw3 = pw3 + 1'b1;
end
ST3: begin
p1_state = ST_MGMT;
p1_channel = '1;
p1_data = '0;
p1_valid = '1;
end
ST_MGMT: begin
if (run) begin
p1_state = ST1;
p1_channel = '0;
p1_valid = '1;
p1_data = pw1;
p_pw1 = pw1 + 1'b1;
end
else begin
p1_state = ST_IDLE;
end
end
endcase
end
always @*
in_readdata = { {31 {1'b0}}, run};
always @(posedge clk or posedge reset) begin
if (reset) begin
run <= '0;
end
else begin
if (in_write) begin
run <= in_writedata[0];
end
else if (pw3_overflow) begin
run <= '0;
end
end
end
always @(posedge clk or posedge reset) begin
if (reset) begin
pw1 <= PW1_RESET[15:0];
pw2 <= PW2_RESET[15:0];
pw3 <= PW3_RESET[7:0];
pw1_overflow <= '0;
pw2_overflow <= '0;
pw3_overflow <= '0;
end
else begin
pw1 <= p_pw1;
pw2 <= p_pw2;
pw3 <= p_pw3;
pw1_overflow <= pw1 == 16'hFFFF;
pw2_overflow <= pw1_overflow && (pw2 == 16'hFFFF);
pw3_overflow <= pw1_overflow && pw2_overflow && (pw3 == PW3_FINAL);
end
end
always @(posedge clk or posedge reset) begin
if (reset) begin
state <= ST_IDLE;
out_channel <= '1;
out_data <= '0;
out_valid <= '0;
end
else begin
state <= p1_state;
out_channel <= p1_channel;
out_data <= p1_data;
out_valid <= p1_valid;
end
end
reg received_req;
always @(posedge clk or posedge reset) begin
if (reset) begin
store_write <= '0;
store_writedata <= '0;
store_addr <= '0;
received_req <= '0;
end
else begin
store_write <= p1_valid;
store_writedata <= p1_data;
if (req_valid)
received_req <= '1;
else if (state == ST_MGMT)
received_req <= '0;
if (p1_valid) begin
if (state == ST_IDLE) begin
store_addr <= '0;
end
else if (!received_req && (state == ST_MGMT)) begin
store_addr &= 6'b1111_00;
end
else begin
store_addr <= store_addr + 6'h1;
end
end
end
end
endmodule
`default_nettype wire
$ cat sim_top.sv
`timescale 1ns/1ns
`default_nettype none
module sim_top;
test_pw_gen_tb tb();
test_program pgm();
endmodule
`default_nettype wire
$ cat test_program.sv
For another episode in which yours truly attempts to become the Tiger Woods of exploit development code golf, click here.
`timescale 1ns/1ns
`default_nettype none
`define CLK sim_top.tb.test_pw_gen_inst_clock_bfm.clk
`define RESET sim_top.tb.test_pw_gen_inst_reset_bfm.reset
`define CSR sim_top.tb.test_pw_gen_inst_csr_bfm
`define OUT sim_top.tb.test_pw_gen_inst_out_bfm
`define REQ sim_top.tb.test_pw_gen_inst_req_bfm
`define DUT sim_top.tb.test_pw_gen_inst.pw_gen_0
module test_program;
import avalon_mm_pkg::*;
initial begin
fork
begin
init();
wait(~`RESET);
repeat(2) @(posedge `CLK);
test_run();
end
begin : drive_req
repeat(100) @(negedge `OUT.sink_channel);
$display("%t: 100 falling edges", $time());
`REQ.set_transaction_data(0);
`REQ.push_transaction();
end
join
@(posedge `CLK);
repeat(10) @(posedge `CLK);
$stop;
end
task automatic init;
`CSR.init();
`OUT.init();
`REQ.init();
endtask
task automatic test_run;
`CSR.set_command_address(0);
`CSR.set_command_data(1, 0);
`CSR.set_command_request(REQ_WRITE);
`CSR.push_command();
repeat(5)
@(posedge `CLK);
repeat(3 * 65536) begin
@(posedge `CLK);
wait(`OUT.signal_transaction_received);
end
repeat(10)
@(posedge `CLK);
endtask
endmodule
`default_nettype wire