summaryrefslogtreecommitdiff
path: root/rtl/cache/monitor.sv
blob: 06864ae71057e15ce135d744db1683814f7e45e3 (plain)
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
`include "cache/defs.sv"

module cache_monitor
(
	input  logic       clk,
	                   rst_n,

	input  addr_tag    core_tag,
	input  addr_index  core_index,
	input  addr_offset core_offset,
	input logic        core_lock,
	input  word        core_writedata,
	output logic[1:0]  core_response,

	input  line        data_rd,

	input  logic       monitor_acquire,
	                   monitor_fail,
	                   monitor_release,
	output line        monitor_update,
	output logic       monitor_commit
);

	// Este módulo provee capacidad para spin_locks (básicamente mutexes) para
	// proteger una sección de código a través de spin lock/unlock.
	// Esto básicamente es la implemenntación de las instrucciones de ARM
	// ldrex, strexeq, que originalmente no son parte ARMv4, esto implica
	// que este quad-core es un frankenstein entre ARMv4 y alguna versión
	// posterior que sí implementa esas instrucciones.


	line monitor_rd, monitor_wr;
	word update_3, update_2, update_1, update_0;
	logic dirty, done, hit, known;
	addr_tag tag;
	addr_index index;

	logic[3:0] mask, mask_clear, core_ex_mask;

	assign monitor_commit = !core_lock || (hit && known && done);
	assign monitor_update = {update_3, update_2, update_1, update_0};

	/* Avalon p. 15:
	 * - 00: OKAY - Successful response for a transaction.
	 * - 10: SLVERR - Error from an endpoint agent. Indicates an unsuccessful transaction.
	 */
	assign core_response = {monitor_fail, 1'b0};

	assign hit = tag == core_tag && index == core_index;
	assign done = monitor_rd == data_rd && mask_clear == 4'b0000;
	assign known = mask[core_offset];
	assign mask_clear = mask & ~core_ex_mask;

	always_comb begin
		{update_3, update_2, update_1, update_0} = monitor_wr;

		unique case (core_offset)
			2'b00: begin
				update_0 = core_writedata;
				core_ex_mask = 4'b0001;
			end

			2'b01: begin
				update_1 = core_writedata;
				core_ex_mask = 4'b0010;
			end

			2'b10: begin
				update_2 = core_writedata;
				core_ex_mask = 4'b0100;
			end

			2'b11: begin
				update_3 = core_writedata;
				core_ex_mask = 4'b1000;
			end
		endcase
	end

	always @(posedge clk or negedge rst_n)
		if (!rst_n) begin
			mask <= 4'b0000;
			dirty <= 0;
		end else begin
			if (monitor_acquire) begin
				mask <= hit && !known && !dirty ? mask | core_ex_mask : core_ex_mask;
				dirty <= 0;
			end

			if (monitor_release) begin
				mask <= hit && known ? mask_clear : 4'b0000;
				dirty <= hit && known;
			end
		end

	always_ff @(posedge clk) begin
		if (monitor_acquire) begin
			tag <= core_tag;
			index <= core_index;

			if (!hit || known || dirty || mask == 4'b0000) begin
				monitor_rd <= data_rd;
				monitor_wr <= data_rd;
			end
		end

		if (monitor_release)
			monitor_wr <= monitor_update;
	end

endmodule