For the condition "trap if data is accessed as code": I know of three ways to do that, (1) tagging memory words, (2) using base and limit registers, and (3) as part of the MMU for virtual memory.
(3) came comparatively late, so we don't need to look at it. I think most architectural families gained (2) under various names (descriptor, segment, address space, base & bound, codewords), at some stage (some late, like x86), and it would be interesting to make list. Note that (2) is primarily used for timesharing/multiprocessing, and not necessarily to distinguish between code and data. For example, the CDC 6000 only had a single pair (RA "reference address" and FL "field length").
(1) grew naturally out of various error detection schemes for memory (e.g. parity), which already did trap, so it was an obvious extension. There were also variants in early machines which had no tags on memory words, but mark or sentinel words, e.g. to describe the end of an array, which also would trap on access (but weren't really a code/data distinction, or general protection scheme).
This article has a bit of historical overview of tagged systems in the section II "Previous Work" on page 3.
Let's look at some early systems in detail:
Bourrough B5000 (1961): The B5000 implemented both (1) and (2). In the original B5000 and B5500 implementation (see manual), not all memory words were tagged (p. 2-5): (Packed) character words didn't have a tag, but control words and numerical words had a visible flag bit as part of the normal data bits. Descriptors were used extensively, even for single arrays, and the trap for out-of-bound descriptor access was identical to the trap for control-word-instead-of-numeric-word access.
The B5000 was unconventional in the sense that the instruction set and principles of operation were aimed at implementing a high level language. But it did not implement the principle of Lisp machines and later variants like the B6500 that the tag bits also described data types. So in that sense the B5000 does exactly what the BESM-6 does (descriptors aside).
One of the principal engineers of the B5000 also worked on the
Rice computer (R1) (built 1958-1961, partly operational in 1959): The Rice Computer again implemented both (1) and (2). I couldn't find a manual, but this page describes some details. The instruction set of the Rice Computer was more conventional than the B5000, though still a bit odd. Memory was initially CRT storage tubes of 8192 bits each. Word length was 63 bits: 54 bits of data, 7 bits of Hamming error-correction code, and 2 tag bits. The error correction was needed because the storage tubes were very unreliable (bits would gets weaker when read, tight loops would cause read errors). One can see how tag bits come natural when there's already that many error correction bits.
The archtectural roots of the Rice Computer were described as the MANIAC II at Los Alamos, and the Brookhaven computer at Brookhaven National Laboratory. I couldn't find anything on the latter.
Details on the former are also hard to find. It used an additional 49th bit on top of the 48 data bits in storage, but that was probably parity. The Maniac III included a tag bit at least in the opcode.